Color Zerolag TriX OSMA 戦略
概要
この戦略は、5つの異なるTRIX期間から構築されたゼロラグTRIX OSMAオシレーターを使用します。各TRIXコンポーネントは重み付けされ平滑化されて、最小限のラグでトレンド変化に反応する単一のオシレーターを形成します。オシレーターが上向きに転換するとロングポジションが開かれ、下向きに転換するとショートポジションが開かれます。
仕組み
- 三重指数移動平均と変化率を使用して5つのTRIX値を計算します。
- TRIXの値をそれぞれの重みと組み合わせて、高速トレンド値を形成します。
- 高速トレンドを2回平滑化して、ゼロラグOSMAオシレーターを作成します。
- 最後の2つのオシレーター値を比較してトレンド転換を検出します。
- 上向き転換でロング、下向き転換でショートに入ります。新しいポジションを開く前に、既存の反対ポジションはクローズされます。
パラメーター
Smoothing1– 低速トレンドの平滑化係数。Smoothing2– OSMA ラインの平滑化係数。Factor1..Factor5– 各TRIXコンポーネントに適用される重み。Period1..Period5– 5つのTRIX計算の期間。CandleType– 計算に使用するローソク足シリーズ。
インジケーター
- TripleExponentialMovingAverage
- RateOfChange
- カスタムゼロラグTRIX OSMA組み合わせ
注意事項
この戦略はシグナルを生成する前に5つのTRIXインジケーターすべてが形成されている必要があります。ストップとターゲットの保護は StartProtection を通じて有効化されます。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>Five weighted TRIX components with slow trend and smoothed OSMA reversal line.</summary>
public class ColorZerolagTrixOsmaStrategy : Strategy
{
private sealed class TrixState
{
private readonly int _period;
private readonly decimal _alpha;
private decimal _first;
private decimal _second;
private decimal _third;
private decimal _previousThird;
private int _samples;
public TrixState(int period)
{
_period = period;
_alpha = 2m / (period + 1m);
}
// The replay harness clones and resets a strategy, then checks nested fields
// structurally. Internal TRIX state must compare by value, not reference.
public override bool Equals(object other)
=> other is TrixState value
&& _period == value._period
&& _alpha == value._alpha
&& _first == value._first
&& _second == value._second
&& _third == value._third
&& _previousThird == value._previousThird
&& _samples == value._samples;
public override int GetHashCode()
=> HashCode.Combine(_period, _alpha, _first, _second, _third, _previousThird, _samples);
public decimal? Add(decimal close)
{
if (_samples == 0)
{
_first = _second = _third = close;
_previousThird = _third;
_samples = 1;
return null;
}
_first += _alpha * (close - _first);
_second += _alpha * (_first - _second);
_third += _alpha * (_second - _third);
_samples++;
var value = _previousThird == 0m ? 0m
: 100m * (_third - _previousThird) / _previousThird;
_previousThird = _third;
// Each EMA stage needs history: do not trade during triple-EMA warmup.
return _samples >= 3 * _period + 1 ? value : null;
}
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smoothing1, _smoothing2;
private readonly StrategyParam<decimal> _stopLoss, _takeProfit;
private readonly StrategyParam<int>[] _periods = new StrategyParam<int>[5];
private readonly StrategyParam<decimal>[] _factors = new StrategyParam<decimal>[5];
private readonly StrategyParam<bool> _buyOpen, _buyClose, _sellOpen, _sellClose;
private TrixState[] _trix;
private decimal? _slow, _smoothedOsma, _previousOsma, _beforePreviousOsma;
private Order _pending;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Smoothing1 { get => _smoothing1.Value; set => _smoothing1.Value = value; }
public int Smoothing2 { get => _smoothing2.Value; set => _smoothing2.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public int Period1 { get => _periods[0].Value; set => _periods[0].Value = value; }
public int Period2 { get => _periods[1].Value; set => _periods[1].Value = value; }
public int Period3 { get => _periods[2].Value; set => _periods[2].Value = value; }
public int Period4 { get => _periods[3].Value; set => _periods[3].Value = value; }
public int Period5 { get => _periods[4].Value; set => _periods[4].Value = value; }
public decimal Factor1 { get => _factors[0].Value; set => _factors[0].Value = value; }
public decimal Factor2 { get => _factors[1].Value; set => _factors[1].Value = value; }
public decimal Factor3 { get => _factors[2].Value; set => _factors[2].Value = value; }
public decimal Factor4 { get => _factors[3].Value; set => _factors[3].Value = value; }
public decimal Factor5 { get => _factors[4].Value; set => _factors[4].Value = value; }
public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }
public ColorZerolagTrixOsmaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Signal candle timeframe", "General");
_smoothing1 = Param(nameof(Smoothing1), 9).SetGreaterThanZero()
.SetDisplay("Smoothing 1", "Slow-trend EMA period", "Indicator");
_smoothing2 = Param(nameof(Smoothing2), 5).SetGreaterThanZero()
.SetDisplay("Smoothing 2", "OSMA-line EMA period", "Indicator");
_stopLoss = Param(nameof(StopLoss), 0m).SetNotNegative()
.SetDisplay("Stop Loss", "Optional absolute stop distance", "Risk");
_takeProfit = Param(nameof(TakeProfit), 0m).SetNotNegative()
.SetDisplay("Take Profit", "Optional absolute take-profit distance", "Risk");
var periods = new[] { 5, 8, 13, 21, 34 };
for (var i = 0; i < 5; i++)
{
_periods[i] = Param("Period" + (i + 1), periods[i]).SetGreaterThanZero()
.SetDisplay("TRIX period " + (i + 1), "Triple EMA period", "Indicator");
_factors[i] = Param("Factor" + (i + 1), 1m)
.SetDisplay("TRIX factor " + (i + 1), "Component weight", "Indicator");
}
_buyOpen = Param(nameof(BuyOpen), true).SetDisplay("Buy Open", "Enable long entries", "Trading");
_buyClose = Param(nameof(BuyClose), true).SetDisplay("Buy Close", "Enable long exits", "Trading");
_sellOpen = Param(nameof(SellOpen), true).SetDisplay("Sell Open", "Enable short entries", "Trading");
_sellClose = Param(nameof(SellClose), true).SetDisplay("Sell Close", "Enable short exits", "Trading");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
private void ResetHistory()
{
_trix = new TrixState[5];
for (var i = 0; i < 5; i++)
_trix[i] = new TrixState(_periods[i].Value);
_slow = _smoothedOsma = _previousOsma = _beforePreviousOsma = null;
_pending = null;
}
protected override void OnReseted()
{
base.OnReseted();
ResetHistory();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetHistory();
StartProtection(takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss, UnitTypes.Absolute));
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var fast = 0m;
var formed = true;
for (var i = 0; i < _trix.Length; i++)
{
var value = _trix[i].Add(candle.ClosePrice);
if (value is decimal trix)
fast += trix * _factors[i].Value;
else
formed = false;
}
if (!formed)
return;
var slow = _slow is decimal oldSlow
? oldSlow + 2m / (Smoothing1 + 1m) * (fast - oldSlow)
: fast;
var osma = fast - slow;
var currentOsma = _smoothedOsma is decimal oldOsma
? oldOsma + 2m / (Smoothing2 + 1m) * (osma - oldOsma)
: osma;
var buy = _beforePreviousOsma is decimal older && _previousOsma is decimal previous
&& previous < older && currentOsma > previous;
var sell = _beforePreviousOsma is decimal olderDown && _previousOsma is decimal previousDown
&& previousDown > olderDown && currentOsma < previousDown;
_beforePreviousOsma = _previousOsma;
_previousOsma = _smoothedOsma = currentOsma;
_slow = slow;
if (!IsFormedAndOnlineAndAllowTrading()
|| (_pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed)))
return;
if (buy)
{
if (Position < 0m && SellClose)
_pending = BuyMarket(-Position + (BuyOpen ? Volume : 0m));
else if (Position == 0m && BuyOpen)
_pending = BuyMarket(Volume);
}
else if (sell)
{
if (Position > 0m && BuyClose)
_pending = SellMarket(Position + (SellOpen ? Volume : 0m));
else if (Position == 0m && SellOpen)
_pending = SellMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, OrderStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class _TrixState:
def __init__(self, period):
self.period = period
self.alpha = 2.0 / (period + 1.0)
self.first = self.second = self.third = self.previous = None
self.count = 0
def add(self, close):
if self.count == 0:
self.first = self.second = self.third = self.previous = close
self.count = 1
return None
self.first += self.alpha * (close - self.first)
self.second += self.alpha * (self.first - self.second)
self.third += self.alpha * (self.second - self.third)
self.count += 1
trix = 100.0 * (self.third - self.previous) / self.previous if self.previous != 0 else 0.0
self.previous = self.third
return trix if self.count >= 3 * self.period + 1 else None
class color_zerolag_trix_osma_strategy(Strategy):
"""Weighted five-component TRIX OSMA, two smoothing stages and slope reversals."""
def __init__(self):
super(color_zerolag_trix_osma_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Signal candle timeframe", "General")
self._smoothing1 = self.Param("Smoothing1", 9).SetGreaterThanZero() \
.SetDisplay("Smoothing 1", "Slow-trend EMA period", "Indicator")
self._smoothing2 = self.Param("Smoothing2", 5).SetGreaterThanZero() \
.SetDisplay("Smoothing 2", "OSMA-line EMA period", "Indicator")
self._stop_loss = self.Param("StopLoss", 0.0).SetNotNegative() \
.SetDisplay("Stop Loss", "Optional absolute stop distance", "Risk")
self._take_profit = self.Param("TakeProfit", 0.0).SetNotNegative() \
.SetDisplay("Take Profit", "Optional absolute target distance", "Risk")
self._periods = []
self._factors = []
for index, period in enumerate((5, 8, 13, 21, 34), start=1):
self._periods.append(self.Param("Period" + str(index), period).SetGreaterThanZero()
.SetDisplay("TRIX Period " + str(index), "Triple EMA period", "Indicator"))
self._factors.append(self.Param("Factor" + str(index), 1.0)
.SetDisplay("TRIX Factor " + str(index), "Component weight", "Indicator"))
self._buy_open = self.Param("BuyOpen", True).SetDisplay("Buy Open", "Enable long entries", "Trading")
self._buy_close = self.Param("BuyClose", True).SetDisplay("Buy Close", "Enable long exits", "Trading")
self._sell_open = self.Param("SellOpen", True).SetDisplay("Sell Open", "Enable short entries", "Trading")
self._sell_close = self.Param("SellClose", True).SetDisplay("Sell Close", "Enable short exits", "Trading")
self._reset_history()
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_history(self):
self._trix = [_TrixState(int(p.Value)) for p in self._periods]
self._slow = self._smoothed_osma = self._previous_osma = self._before_previous_osma = None
self._pending = None
def OnReseted(self):
super(color_zerolag_trix_osma_strategy, self).OnReseted()
self._reset_history()
def OnStarted2(self, time):
super(color_zerolag_trix_osma_strategy, self).OnStarted2(time)
self._reset_history()
self.StartProtection(takeProfit=Unit(self._take_profit.Value, UnitTypes.Absolute),
stopLoss=Unit(self._stop_loss.Value, UnitTypes.Absolute))
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
values = [trix.add(float(candle.ClosePrice)) for trix in self._trix]
if any(x is None for x in values):
return
fast = sum(value * float(param.Value) for value, param in zip(values, self._factors))
slow = fast if self._slow is None else self._slow + 2.0 / (int(self._smoothing1.Value) + 1.0) * (fast - self._slow)
osma = fast - slow
value = osma if self._smoothed_osma is None else self._smoothed_osma + 2.0 / (int(self._smoothing2.Value) + 1.0) * (osma - self._smoothed_osma)
buy = self._before_previous_osma is not None and self._previous_osma < self._before_previous_osma and value > self._previous_osma
sell = self._before_previous_osma is not None and self._previous_osma > self._before_previous_osma and value < self._previous_osma
self._before_previous_osma, self._previous_osma, self._smoothed_osma, self._slow = self._previous_osma, value, value, slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed):
return
if buy:
if self.Position < 0 and self._sell_close.Value:
self._pending = self.BuyMarket(-self.Position + (self.Volume if self._buy_open.Value else 0))
elif self.Position == 0 and self._buy_open.Value:
self._pending = self.BuyMarket(self.Volume)
elif sell:
if self.Position > 0 and self._buy_close.Value:
self._pending = self.SellMarket(self.Position + (self.Volume if self._sell_open.Value else 0))
elif self.Position == 0 and self._sell_open.Value:
self._pending = self.SellMarket(self.Volume)
def CreateClone(self):
return color_zerolag_trix_osma_strategy()