適応型トレンドフロー戦略
適応型トレンドフロー戦略は、典型価格の高速・低速EMAからボラティリティベースのチャネルを構築します。価格がチャネルの境界を越えると内部トレンドが反転します。トレンドが上向きになりオプションのSMAおよびMACDフィルターが確認したとき、ロングポジションを開きます。トレンドが下向きに反転したときにポジションを閉じます。
詳細
- エントリー条件:
- トレンドが下降から上昇に変わり、フィルターが確認する。
- ロング/ショート: ロングのみ。
- エグジット条件:
- トレンドが上昇から下降に変わる。
- ストップ: なし。
- デフォルト値:
Length= 2SmoothLength= 2Sensitivity= 2.0UseSmaFilter= trueSmaLength= 4UseMacdFilter= trueMacdFastLength= 2MacdSlowLength= 7MacdSignalLength= 2
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: ロングのみ
- インジケーター: EMA, SMA, MACD, Standard Deviation
- ストップ: いいえ
- 複雑さ: 中程度
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Adaptive Trend Flow strategy.
/// The basis is the mean of EMA(Length) and EMA(2 * Length) of the typical price; the channel adds and subtracts Sensitivity times
/// an EMA(SmoothLength) of the typical price standard deviation over Length bars. A close above the upper band turns the trend up,
/// a close below the lower band turns it down. The strategy buys when the trend turns from down to up while the close is above
/// SMA(SmaLength) and the MACD line is above its signal (each filter optional), and closes the long when the trend turns down.
/// </summary>
public class AdaptiveTrendFlowStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<int> _smoothLength;
private readonly StrategyParam<decimal> _sensitivity;
private readonly StrategyParam<bool> _useSmaFilter;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<bool> _useMacdFilter;
private readonly StrategyParam<int> _macdFastLength;
private readonly StrategyParam<int> _macdSlowLength;
private readonly StrategyParam<int> _macdSignalLength;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private StandardDeviation _stdDev;
private ExponentialMovingAverage _volSmooth;
private int _trend;
/// <summary>
/// Fast EMA and deviation length; the slow EMA uses twice this length.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// EMA length that smooths the deviation.
/// </summary>
public int SmoothLength
{
get => _smoothLength.Value;
set => _smoothLength.Value = value;
}
/// <summary>
/// Deviation multiplier of the channel.
/// </summary>
public decimal Sensitivity
{
get => _sensitivity.Value;
set => _sensitivity.Value = value;
}
/// <summary>
/// Require the close above the SMA.
/// </summary>
public bool UseSmaFilter
{
get => _useSmaFilter.Value;
set => _useSmaFilter.Value = value;
}
/// <summary>
/// SMA period of the filter.
/// </summary>
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
/// <summary>
/// Require the MACD line above its signal.
/// </summary>
public bool UseMacdFilter
{
get => _useMacdFilter.Value;
set => _useMacdFilter.Value = value;
}
/// <summary>
/// MACD fast EMA length.
/// </summary>
public int MacdFastLength
{
get => _macdFastLength.Value;
set => _macdFastLength.Value = value;
}
/// <summary>
/// MACD slow EMA length.
/// </summary>
public int MacdSlowLength
{
get => _macdSlowLength.Value;
set => _macdSlowLength.Value = value;
}
/// <summary>
/// MACD signal EMA length.
/// </summary>
public int MacdSignalLength
{
get => _macdSignalLength.Value;
set => _macdSignalLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AdaptiveTrendFlowStrategy()
{
_length = Param(nameof(Length), 2)
.SetGreaterThanZero()
.SetDisplay("Length", "Fast EMA and deviation length; the slow EMA uses twice this length", "Trend");
_smoothLength = Param(nameof(SmoothLength), 2)
.SetGreaterThanZero()
.SetDisplay("Smooth Length", "EMA length that smooths the deviation", "Trend");
_sensitivity = Param(nameof(Sensitivity), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Sensitivity", "Deviation multiplier of the channel", "Trend");
_useSmaFilter = Param(nameof(UseSmaFilter), true)
.SetDisplay("Use SMA Filter", "Require the close above the SMA", "Filters");
_smaLength = Param(nameof(SmaLength), 4)
.SetGreaterThanZero()
.SetDisplay("SMA Length", "SMA period of the filter", "Filters");
_useMacdFilter = Param(nameof(UseMacdFilter), true)
.SetDisplay("Use MACD Filter", "Require the MACD line above its signal", "Filters");
_macdFastLength = Param(nameof(MacdFastLength), 2)
.SetGreaterThanZero()
.SetDisplay("MACD Fast Length", "MACD fast EMA length", "Filters");
_macdSlowLength = Param(nameof(MacdSlowLength), 7)
.SetGreaterThanZero()
.SetDisplay("MACD Slow Length", "MACD slow EMA length", "Filters");
_macdSignalLength = Param(nameof(MacdSignalLength), 2)
.SetGreaterThanZero()
.SetDisplay("MACD Signal Length", "MACD signal EMA length", "Filters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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();
_fastEma = null;
_slowEma = null;
_stdDev = null;
_volSmooth = null;
_trend = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_trend = 0;
_fastEma = new ExponentialMovingAverage { Length = Length };
_slowEma = new ExponentialMovingAverage { Length = Length * 2 };
_stdDev = new StandardDeviation { Length = Length };
_volSmooth = new ExponentialMovingAverage { Length = SmoothLength };
var sma = new SimpleMovingAverage { Length = SmaLength };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastLength },
LongMa = { Length = MacdSlowLength },
},
SignalMa = { Length = MacdSignalLength },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
var typical = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
var time = candle.ServerTime;
var fast = _fastEma.Process(typical, time, true).ToDecimal();
var slow = _slowEma.Process(typical, time, true).ToDecimal();
var deviation = _stdDev.Process(typical, time, true);
if (!_fastEma.IsFormed || !_slowEma.IsFormed || !_stdDev.IsFormed)
return;
var smoothVol = _volSmooth.Process(deviation.ToDecimal(), time, true).ToDecimal();
if (!_volSmooth.IsFormed)
return;
var basis = (fast + slow) / 2m;
var upper = basis + smoothVol * Sensitivity;
var lower = basis - smoothVol * Sensitivity;
var close = candle.ClosePrice;
var prevTrend = _trend;
if (close > upper)
_trend = 1;
else if (close < lower)
_trend = -1;
if (!smaValue.IsFormed || !macdValue.IsFormed)
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue { Macd: decimal macdLine, Signal: decimal signalLine })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var smaOk = !UseSmaFilter || close > smaValue.ToDecimal();
var macdOk = !UseMacdFilter || macdLine > signalLine;
if (prevTrend == -1 && _trend == 1 && smaOk && macdOk && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (prevTrend == 1 && _trend == -1 && Position > 0)
SellMarket(Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, StandardDeviation, SimpleMovingAverage, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class adaptive_trend_flow_strategy(Strategy):
"""
Adaptive Trend Flow strategy.
The basis is the mean of EMA(Length) and EMA(2 * Length) of the typical price; the channel adds and subtracts Sensitivity times
an EMA(SmoothLength) of the typical price standard deviation over Length bars. A close above the upper band turns the trend up,
a close below the lower band turns it down. The strategy buys when the trend turns from down to up while the close is above
SMA(SmaLength) and the MACD line is above its signal (each filter optional), and closes the long when the trend turns down.
"""
def __init__(self):
super(adaptive_trend_flow_strategy, self).__init__()
self._length = self.Param("Length", 2).SetGreaterThanZero().SetDisplay("Length", "Fast EMA and deviation length; the slow EMA uses twice this length", "Trend")
self._smooth_length = self.Param("SmoothLength", 2).SetGreaterThanZero().SetDisplay("Smooth Length", "EMA length that smooths the deviation", "Trend")
self._sensitivity = self.Param("Sensitivity", 2.0).SetGreaterThanZero().SetDisplay("Sensitivity", "Deviation multiplier of the channel", "Trend")
self._use_sma_filter = self.Param("UseSmaFilter", True).SetDisplay("Use SMA Filter", "Require the close above the SMA", "Filters")
self._sma_length = self.Param("SmaLength", 4).SetGreaterThanZero().SetDisplay("SMA Length", "SMA period of the filter", "Filters")
self._use_macd_filter = self.Param("UseMacdFilter", True).SetDisplay("Use MACD Filter", "Require the MACD line above its signal", "Filters")
self._macd_fast_length = self.Param("MacdFastLength", 2).SetGreaterThanZero().SetDisplay("MACD Fast Length", "MACD fast EMA length", "Filters")
self._macd_slow_length = self.Param("MacdSlowLength", 7).SetGreaterThanZero().SetDisplay("MACD Slow Length", "MACD slow EMA length", "Filters")
self._macd_signal_length = self.Param("MacdSignalLength", 2).SetGreaterThanZero().SetDisplay("MACD Signal Length", "MACD signal EMA length", "Filters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._fast_ema = None
self._slow_ema = None
self._std_dev = None
self._vol_smooth = None
self._trend = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_trend_flow_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._std_dev = None
self._vol_smooth = None
self._trend = 0
def OnStarted2(self, time):
super(adaptive_trend_flow_strategy, self).OnStarted2(time)
length = self._length.Value
self._trend = 0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = length
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = length * 2
self._std_dev = StandardDeviation()
self._std_dev.Length = length
self._vol_smooth = ExponentialMovingAverage()
self._vol_smooth.Length = self._smooth_length.Value
sma = SimpleMovingAverage()
sma.Length = self._sma_length.Value
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast_length.Value
macd.Macd.LongMa.Length = self._macd_slow_length.Value
macd.SignalMa.Length = self._macd_signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, macd, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_value, macd_value):
if candle.State != CandleStates.Finished:
return
typical = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / Decimal(3)
time = candle.ServerTime
fast = process_value(self._fast_ema, typical, time, True).GetValue[Decimal](None)
slow = process_value(self._slow_ema, typical, time, True).GetValue[Decimal](None)
deviation = process_value(self._std_dev, typical, time, True)
if not self._fast_ema.IsFormed or not self._slow_ema.IsFormed or not self._std_dev.IsFormed:
return
smooth_vol = process_value(self._vol_smooth, deviation.GetValue[Decimal](None), time, True).GetValue[Decimal](None)
if not self._vol_smooth.IsFormed:
return
sensitivity = Decimal(self._sensitivity.Value)
basis = (fast + slow) / Decimal(2)
upper = basis + smooth_vol * sensitivity
lower = basis - smooth_vol * sensitivity
close = candle.ClosePrice
prev_trend = self._trend
if close > upper:
self._trend = 1
elif close < lower:
self._trend = -1
if not sma_value.IsFormed or not macd_value.IsFormed:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
sma_ok = not self._use_sma_filter.Value or close > sma_value.GetValue[Decimal](None)
macd_ok = not self._use_macd_filter.Value or macd_value.Macd > macd_value.Signal
if prev_trend == -1 and self._trend == 1 and sma_ok and macd_ok and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif prev_trend == 1 and self._trend == -1 and self.Position > 0:
self.SellMarket(self.Position)
def CreateClone(self):
return adaptive_trend_flow_strategy()