DEMAトレンドオシレーター戦略
この戦略は二重指数移動平均(DEMA)を移動平均と標準偏差で正規化します。正規化された値がロング閾値を超え、価格が上部バンドを上回っているときにロングポジションを取ります。正規化された値がショート閾値を下回り、価格が下部バンドを下回っているときにショートポジションを取ります。ATRベースのトレーリングストップ、バンドストップロス、リスクリワードベースのテイクプロフィットを使用します。
詳細
- エントリー条件:
- ロング: 正規化値 >
LongThresholdかつ 安値 > 上部バンド - ショート: 正規化値 <
ShortThresholdかつ 高値 < 下部バンド
- ロング: 正規化値 >
- ロング/ショート: 両方
- エグジット条件:
- ロング: 価格がテイクプロフィット、バンドストップロス、またはトレーリングストップに到達
- ショート: 価格がテイクプロフィット、バンドストップロス、またはトレーリングストップに到達
- ストップ: バンドストップロス、ATRトレーリング、リスクリワードテイクプロフィット
- デフォルト値:
DemaPeriod= 40BaseLength= 20LongThreshold= 55mShortThreshold= 45mRiskReward= 1.5mAtrMultiplier= 2mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: DEMA, SMA, StandardDeviation, ATR
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// DEMA trend oscillator strategy.
/// The DEMA is normalized against its BaseLength SMA and standard deviation: the z-score is mapped to 0..100 with a logistic curve.
/// The bands are the DEMA plus and minus that standard deviation.
/// A long opens when the normalized value is above LongThreshold and the whole candle is above the upper band;
/// a short opens when it is below ShortThreshold and the whole candle is below the lower band.
/// The stop is the opposite band at entry, the target is RiskReward times that risk,
/// and an ATR trailing stop (AtrMultiplier times ATR) follows the price.
/// </summary>
public class DemaTrendOscillatorStrategy : Strategy
{
private const int _atrLength = 14;
private readonly StrategyParam<int> _demaPeriod;
private readonly StrategyParam<int> _baseLength;
private readonly StrategyParam<decimal> _longThreshold;
private readonly StrategyParam<decimal> _shortThreshold;
private readonly StrategyParam<decimal> _riskReward;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _base;
private StandardDeviation _stdDev;
private decimal? _stopPrice;
private decimal? _takePrice;
private decimal? _trailPrice;
/// <summary>
/// DEMA period.
/// </summary>
public int DemaPeriod { get => _demaPeriod.Value; set => _demaPeriod.Value = value; }
/// <summary>
/// Length of the SMA and standard deviation of the DEMA.
/// </summary>
public int BaseLength { get => _baseLength.Value; set => _baseLength.Value = value; }
/// <summary>
/// Normalized value above which longs are allowed.
/// </summary>
public decimal LongThreshold { get => _longThreshold.Value; set => _longThreshold.Value = value; }
/// <summary>
/// Normalized value below which shorts are allowed.
/// </summary>
public decimal ShortThreshold { get => _shortThreshold.Value; set => _shortThreshold.Value = value; }
/// <summary>
/// Take profit as a multiple of the band stop distance.
/// </summary>
public decimal RiskReward { get => _riskReward.Value; set => _riskReward.Value = value; }
/// <summary>
/// ATR multiplier of the trailing stop.
/// </summary>
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public DemaTrendOscillatorStrategy()
{
_demaPeriod = Param(nameof(DemaPeriod), 40)
.SetGreaterThanZero()
.SetDisplay("DEMA Period", "DEMA period", "Indicators");
_baseLength = Param(nameof(BaseLength), 20)
.SetGreaterThanZero()
.SetDisplay("Base Length", "Length of the SMA and standard deviation of the DEMA", "Indicators");
_longThreshold = Param(nameof(LongThreshold), 55m)
.SetDisplay("Long Threshold", "Normalized value above which longs are allowed", "Signals");
_shortThreshold = Param(nameof(ShortThreshold), 45m)
.SetDisplay("Short Threshold", "Normalized value below which shorts are allowed", "Signals");
_riskReward = Param(nameof(RiskReward), 1.5m)
.SetNotNegative()
.SetDisplay("Risk Reward", "Take profit as a multiple of the band stop distance", "Risk");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("ATR Multiplier", "ATR multiplier of the trailing stop", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ClearStops();
}
private void ClearStops()
{
_stopPrice = null;
_takePrice = null;
_trailPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ClearStops();
var dema = new DoubleExponentialMovingAverage { Length = DemaPeriod };
var atr = new AverageTrueRange { Length = _atrLength };
_base = new SimpleMovingAverage { Length = BaseLength };
_stdDev = new StandardDeviation { Length = BaseLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(dema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, dema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue demaValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!demaValue.IsFormed)
return;
var dema = demaValue.GetValue<decimal>();
var baseValue = _base.Process(dema, candle.ServerTime, true);
var sdValue = _stdDev.Process(dema, candle.ServerTime, true);
if (!baseValue.IsFormed || !sdValue.IsFormed || !atrValue.IsFormed)
return;
var basis = baseValue.GetValue<decimal>();
var sd = sdValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
if (sd <= 0m)
return;
var z = (double)((dema - basis) / sd);
var normalized = (decimal)(100.0 / (1.0 + Math.Exp(-z)));
var upper = dema + sd;
var lower = dema - sd;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0)
{
if (AtrMultiplier > 0m)
{
var trail = close - atr * AtrMultiplier;
_trailPrice = _trailPrice is decimal t ? Math.Max(t, trail) : trail;
}
if ((_stopPrice is decimal stop && candle.LowPrice <= stop)
|| (_takePrice is decimal take && candle.HighPrice >= take)
|| (_trailPrice is decimal trailStop && candle.LowPrice <= trailStop))
{
SellMarket(Position);
ClearStops();
}
return;
}
if (Position < 0)
{
if (AtrMultiplier > 0m)
{
var trail = close + atr * AtrMultiplier;
_trailPrice = _trailPrice is decimal t ? Math.Min(t, trail) : trail;
}
if ((_stopPrice is decimal stop && candle.HighPrice >= stop)
|| (_takePrice is decimal take && candle.LowPrice <= take)
|| (_trailPrice is decimal trailStop && candle.HighPrice >= trailStop))
{
BuyMarket(-Position);
ClearStops();
}
return;
}
if (normalized > LongThreshold && candle.LowPrice > upper)
{
BuyMarket();
_stopPrice = lower;
_takePrice = RiskReward > 0m ? close + (close - lower) * RiskReward : null;
_trailPrice = null;
}
else if (normalized < ShortThreshold && candle.HighPrice < lower)
{
SellMarket();
_stopPrice = upper;
_takePrice = RiskReward > 0m ? close - (upper - close) * RiskReward : null;
_trailPrice = null;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
import math
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import DoubleExponentialMovingAverage, AverageTrueRange, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
ATR_LENGTH = 14
class dema_trend_oscillator_strategy(Strategy):
"""
DEMA trend oscillator strategy.
The DEMA is normalized against its BaseLength SMA and standard deviation: the z-score is mapped to 0..100 with a logistic curve.
The bands are the DEMA plus and minus that standard deviation.
A long opens when the normalized value is above LongThreshold and the whole candle is above the upper band;
a short opens when it is below ShortThreshold and the whole candle is below the lower band.
The stop is the opposite band at entry, the target is RiskReward times that risk,
and an ATR trailing stop (AtrMultiplier times ATR) follows the price.
"""
def __init__(self):
super(dema_trend_oscillator_strategy, self).__init__()
self._dema_period = self.Param("DemaPeriod", 40).SetGreaterThanZero().SetDisplay("DEMA Period", "DEMA period", "Indicators")
self._base_length = self.Param("BaseLength", 20).SetGreaterThanZero().SetDisplay("Base Length", "Length of the SMA and standard deviation of the DEMA", "Indicators")
self._long_threshold = self.Param("LongThreshold", 55.0).SetDisplay("Long Threshold", "Normalized value above which longs are allowed", "Signals")
self._short_threshold = self.Param("ShortThreshold", 45.0).SetDisplay("Short Threshold", "Normalized value below which shorts are allowed", "Signals")
self._risk_reward = self.Param("RiskReward", 1.5).SetNotNegative().SetDisplay("Risk Reward", "Take profit as a multiple of the band stop distance", "Risk")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetNotNegative().SetDisplay("ATR Multiplier", "ATR multiplier of the trailing stop", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._base = None
self._std_dev = None
self._clear_stops()
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def _clear_stops(self):
self._stop_price = None
self._take_price = None
self._trail_price = None
def OnReseted(self):
super(dema_trend_oscillator_strategy, self).OnReseted()
self._clear_stops()
def OnStarted2(self, time):
super(dema_trend_oscillator_strategy, self).OnStarted2(time)
self._clear_stops()
dema = DoubleExponentialMovingAverage()
dema.Length = self._dema_period.Value
atr = AverageTrueRange()
atr.Length = ATR_LENGTH
self._base = SimpleMovingAverage()
self._base.Length = self._base_length.Value
self._std_dev = StandardDeviation()
self._std_dev.Length = self._base_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(dema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, dema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, dema_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not dema_value.IsFormed:
return
dema_dec = dema_value.GetValue[Decimal](None)
base_value = process_float(self._base, dema_dec, candle.ServerTime, True)
sd_value = process_float(self._std_dev, dema_dec, candle.ServerTime, True)
if not base_value.IsFormed or not sd_value.IsFormed or not atr_value.IsFormed:
return
dema = float(dema_dec)
basis = float(base_value.GetValue[Decimal](None))
sd = float(sd_value.GetValue[Decimal](None))
atr = float(atr_value.GetValue[Decimal](None))
if sd <= 0:
return
z = (dema - basis) / sd
normalized = 100.0 / (1.0 + math.exp(-z))
upper = dema + sd
lower = dema - sd
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
atr_mult = float(self._atr_multiplier.Value)
risk_reward = float(self._risk_reward.Value)
if self.Position > 0:
if atr_mult > 0:
trail = close - atr * atr_mult
self._trail_price = trail if self._trail_price is None else max(self._trail_price, trail)
if ((self._stop_price is not None and low <= self._stop_price)
or (self._take_price is not None and high >= self._take_price)
or (self._trail_price is not None and low <= self._trail_price)):
self.SellMarket(self.Position)
self._clear_stops()
return
if self.Position < 0:
if atr_mult > 0:
trail = close + atr * atr_mult
self._trail_price = trail if self._trail_price is None else min(self._trail_price, trail)
if ((self._stop_price is not None and high >= self._stop_price)
or (self._take_price is not None and low <= self._take_price)
or (self._trail_price is not None and high >= self._trail_price)):
self.BuyMarket(-self.Position)
self._clear_stops()
return
if normalized > float(self._long_threshold.Value) and low > upper:
self.BuyMarket()
self._stop_price = lower
self._take_price = close + (close - lower) * risk_reward if risk_reward > 0 else None
self._trail_price = None
elif normalized < float(self._short_threshold.Value) and high < lower:
self.SellMarket()
self._stop_price = upper
self._take_price = close - (upper - close) * risk_reward if risk_reward > 0 else None
self._trail_price = None
def CreateClone(self):
return dema_trend_oscillator_strategy()