Color Step Xccx戦略
Color Step XCCXインジケーターに基づく戦略です。インジケーターは平滑化された平均からの価格の偏差を測定し、2本のステップラインを描画します。速い線が遅い線を下回ると買いトレードが開かれます。速い線が遅い線を上回るとショートトレードが開かれます。
詳細
- エントリー条件:
- ロング: 速い線が遅い線を下抜け
- ショート: 速い線が遅い線を上抜け
- ロング/ショート: 両方
- エグジット条件:
- ロング: 速い線が遅い線を上抜け
- ショート: 速い線が遅い線を下抜け
- ストップ: なし
- デフォルト値:
DPeriod= 30MPeriod= 7StepSizeFast= 5StepSizeSlow= 30CandleType= TimeSpan.FromHours(4).TimeFrame()
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: Custom, EMA
- ストップ: なし
- 複雑さ: 中級
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Normalized ColorStepXCCX step channel with JMA price smoothing and T3 deviation
/// smoothing. Long when fast steps below slow, short when fast steps above slow.
/// Unlike a conventional EMA crossover, the step parameters affect BOTH lines.
/// </summary>
public class ColorStepXccxStrategy : Strategy
{
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _mPeriod;
private readonly StrategyParam<decimal> _stepSizeFast;
private readonly StrategyParam<decimal> _stepSizeSlow;
private readonly StrategyParam<DataType> _candleType;
private JurikMovingAverage _priceAverage;
private T3MovingAverage _signedDeviationAverage;
private T3MovingAverage _absoluteDeviationAverage;
private decimal? _previousFast;
private decimal? _previousSlow;
private decimal _fastMin;
private decimal _fastMax;
private decimal _slowMin;
private decimal _slowMax;
private int _fastTrend;
private int _slowTrend;
private bool _hasStepBaseline;
public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
public int MPeriod { get => _mPeriod.Value; set => _mPeriod.Value = value; }
public decimal StepSizeFast { get => _stepSizeFast.Value; set => _stepSizeFast.Value = value; }
public decimal StepSizeSlow { get => _stepSizeSlow.Value; set => _stepSizeSlow.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorStepXccxStrategy()
{
_dPeriod = Param(nameof(DPeriod), 30).SetGreaterThanZero()
.SetDisplay("D Period", "JMA price smoothing period", "Indicators");
_mPeriod = Param(nameof(MPeriod), 7).SetGreaterThanZero()
.SetDisplay("M Period", "T3 normalized deviation smoothing period", "Indicators");
_stepSizeFast = Param(nameof(StepSizeFast), 5m).SetGreaterThanZero()
.SetDisplay("Fast Step", "Normalized oscillator fast step", "Indicators");
_stepSizeSlow = Param(nameof(StepSizeSlow), 30m).SetGreaterThanZero()
.SetDisplay("Slow Step", "Normalized oscillator slow step", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_priceAverage = null;
_signedDeviationAverage = null;
_absoluteDeviationAverage = null;
_previousFast = _previousSlow = null;
_hasStepBaseline = false;
_fastTrend = _slowTrend = 0;
_fastMin = _fastMax = _slowMin = _slowMax = 0m;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousFast = _previousSlow = null;
_hasStepBaseline = false;
_fastTrend = _slowTrend = 0;
_priceAverage = new JurikMovingAverage { Length = DPeriod, Phase = 100 };
// The original T3 smoothing phase (15) represents a factor of 0.15.
_signedDeviationAverage = new T3MovingAverage { Length = MPeriod, VolumeFactor = 0.15m };
_absoluteDeviationAverage = new T3MovingAverage { Length = MPeriod, VolumeFactor = 0.15m };
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// MQL applied-price default is PRICE_TYPICAL, not closing price.
var typical = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
var baseValue = _priceAverage.Process(new DecimalIndicatorValue(_priceAverage, typical, candle.OpenTime) { IsFinal = true });
if (!_priceAverage.IsFormed)
return;
var deviation = typical - baseValue.GetValue<decimal>();
var up = _signedDeviationAverage.Process(new DecimalIndicatorValue(_signedDeviationAverage, deviation, candle.OpenTime) { IsFinal = true });
var down = _absoluteDeviationAverage.Process(new DecimalIndicatorValue(_absoluteDeviationAverage, Math.Abs(deviation), candle.OpenTime) { IsFinal = true });
if (!_signedDeviationAverage.IsFormed || !_absoluteDeviationAverage.IsFormed)
return;
var numerator = up.GetValue<decimal>();
var denominator = down.GetValue<decimal>();
var oscillator = numerator != 0m && denominator != 0m ? 100m * numerator / denominator : 0m;
ProcessNormalized(oscillator);
}
private void ProcessNormalized(decimal oscillator)
{
var fastStep = StepSizeFast;
var slowStep = StepSizeSlow;
var fastMax = oscillator + 2m * fastStep;
var fastMin = oscillator - 2m * fastStep;
var slowMax = oscillator + 2m * slowStep;
var slowMin = oscillator - 2m * slowStep;
if (!_hasStepBaseline)
{
_fastMin = fastMin;
_fastMax = fastMax;
_slowMin = slowMin;
_slowMax = slowMax;
_previousFast = _previousSlow = oscillator;
_hasStepBaseline = true;
return;
}
if (oscillator > _fastMax) _fastTrend = 1;
else if (oscillator < _fastMin) _fastTrend = -1;
if (_fastTrend > 0 && fastMin < _fastMin) fastMin = _fastMin;
if (_fastTrend < 0 && fastMax > _fastMax) fastMax = _fastMax;
if (oscillator > _slowMax) _slowTrend = 1;
else if (oscillator < _slowMin) _slowTrend = -1;
if (_slowTrend > 0 && slowMin < _slowMin) slowMin = _slowMin;
if (_slowTrend < 0 && slowMax > _slowMax) slowMax = _slowMax;
var fast = _fastTrend > 0 ? fastMin + fastStep : fastMax - fastStep;
var slow = _slowTrend > 0 ? slowMin + slowStep : slowMax - slowStep;
var prevFast = _previousFast.Value;
var prevSlow = _previousSlow.Value;
_fastMin = fastMin; _fastMax = fastMax;
_slowMin = slowMin; _slowMax = slowMax;
_previousFast = fast; _previousSlow = slow;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var position = Position;
if (prevFast > prevSlow && fast < slow && position <= 0m)
{
if (position < 0m) BuyMarket(-position);
BuyMarket(Volume);
}
else if (prevFast < prevSlow && fast > slow && position >= 0m)
{
if (position > 0m) SellMarket(position);
SellMarket(Volume);
}
}
}
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 JurikMovingAverage, T3MovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import process_float
class color_step_xccx_strategy(Strategy):
"""Normalized stepped XCCX oscillator, never substituted by EMA crossover."""
def __init__(self):
super(color_step_xccx_strategy, self).__init__()
self._d_period = self.Param("DPeriod", 30).SetDisplay("D Period", "JMA price smoothing period", "Indicators")
self._m_period = self.Param("MPeriod", 7).SetDisplay("M Period", "T3 deviation smoothing period", "Indicators")
self._step_fast = self.Param("StepSizeFast", 5).SetDisplay("Fast Step", "Normalized oscillator fast step", "Indicators")
self._step_slow = self.Param("StepSizeSlow", 30).SetDisplay("Slow Step", "Normalized oscillator slow step", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles", "General")
self._reset_signals()
@property
def DPeriod(self):
return self._d_period.Value
@DPeriod.setter
def DPeriod(self, value):
self._d_period.Value = value
@property
def MPeriod(self):
return self._m_period.Value
@MPeriod.setter
def MPeriod(self, value):
self._m_period.Value = value
@property
def StepSizeFast(self):
return self._step_fast.Value
@StepSizeFast.setter
def StepSizeFast(self, value):
self._step_fast.Value = value
@property
def StepSizeSlow(self):
return self._step_slow.Value
@StepSizeSlow.setter
def StepSizeSlow(self, value):
self._step_slow.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_signals(self):
self._previous_fast = None
self._previous_slow = None
self._fast_min = self._fast_max = 0.0
self._slow_min = self._slow_max = 0.0
self._fast_trend = self._slow_trend = 0
self._has_step_baseline = False
def OnReseted(self):
super(color_step_xccx_strategy, self).OnReseted()
self._reset_signals()
def OnStarted2(self, time):
super(color_step_xccx_strategy, self).OnStarted2(time)
self._reset_signals()
self._price_average = JurikMovingAverage()
self._price_average.Length = self.DPeriod
self._price_average.Phase = 100
self._signed_average = T3MovingAverage()
self._signed_average.Length = self.MPeriod
self._signed_average.VolumeFactor = 0.15
self._absolute_average = T3MovingAverage()
self._absolute_average.Length = self.MPeriod
self._absolute_average.VolumeFactor = 0.15
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
typical = (float(candle.HighPrice) + float(candle.LowPrice) + float(candle.ClosePrice)) / 3.0
baseline = process_float(self._price_average, typical, candle.OpenTime, True)
if not self._price_average.IsFormed:
return
deviation = typical - float(baseline)
up = process_float(self._signed_average, deviation, candle.OpenTime, True)
down = process_float(self._absolute_average, abs(deviation), candle.OpenTime, True)
if not self._signed_average.IsFormed or not self._absolute_average.IsFormed:
return
numerator, denominator = float(up), float(down)
oscillator = 100.0 * numerator / denominator if numerator != 0 and denominator != 0 else 0.0
self._process_normalized(oscillator)
def _process_normalized(self, oscillator):
fast_step = float(self.StepSizeFast)
slow_step = float(self.StepSizeSlow)
if fast_step <= 0 or slow_step <= 0:
raise ValueError('StepSizeFast and StepSizeSlow must be positive')
fast_max, fast_min = oscillator + 2.0 * fast_step, oscillator - 2.0 * fast_step
slow_max, slow_min = oscillator + 2.0 * slow_step, oscillator - 2.0 * slow_step
if not self._has_step_baseline:
self._fast_min, self._fast_max = fast_min, fast_max
self._slow_min, self._slow_max = slow_min, slow_max
self._previous_fast = self._previous_slow = oscillator
self._has_step_baseline = True
return
if oscillator > self._fast_max:
self._fast_trend = 1
elif oscillator < self._fast_min:
self._fast_trend = -1
if self._fast_trend > 0 and fast_min < self._fast_min:
fast_min = self._fast_min
if self._fast_trend < 0 and fast_max > self._fast_max:
fast_max = self._fast_max
if oscillator > self._slow_max:
self._slow_trend = 1
elif oscillator < self._slow_min:
self._slow_trend = -1
if self._slow_trend > 0 and slow_min < self._slow_min:
slow_min = self._slow_min
if self._slow_trend < 0 and slow_max > self._slow_max:
slow_max = self._slow_max
fast = fast_min + fast_step if self._fast_trend > 0 else fast_max - fast_step
slow = slow_min + slow_step if self._slow_trend > 0 else slow_max - slow_step
previous_fast, previous_slow = self._previous_fast, self._previous_slow
self._fast_min, self._fast_max = fast_min, fast_max
self._slow_min, self._slow_max = slow_min, slow_max
self._previous_fast, self._previous_slow = fast, slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
position = self.Position
if previous_fast > previous_slow and fast < slow and position <= 0:
if position < 0:
self.BuyMarket(-position)
self.BuyMarket(self.Volume)
elif previous_fast < previous_slow and fast > slow and position >= 0:
if position > 0:
self.SellMarket(position)
self.SellMarket(self.Volume)
def CreateClone(self):
return color_step_xccx_strategy()