Color Zerolag Momentum OSMA-Strategie
Diese Strategie erstellt einen benutzerdefinierten Zero-Lag-Momentum-OSMA-Oszillator aus fünf Momentum-Berechnungen. Wenn der Oszillatorwert vor zwei Balken unter dem Wert vor drei Balken liegt, gilt der Trend als aufwärtsgerichtet. In diesem Fall werden Short-Positionen geschlossen und eine neue Long-Position kann eröffnet werden, wenn der aktuellste Wert über dem Wert vor zwei Balken liegt. Wenn der Wert vor zwei Balken über dem Wert vor drei Balken liegt, ist der Trend abwärtsgerichtet, Long-Positionen werden geschlossen, und eine Short-Position kann eröffnet werden, wenn der letzte Wert unter dem Wert vor zwei Balken liegt.
Parameter
Smoothing1– erster Glättungsfaktor für den langsamen Trend.Smoothing2– zweiter Glättungsfaktor für die OSMA-Linie.Factor1-5– Gewichte für jede Momentum-Komponente.MomentumPeriod1-5– Perioden für die Momentum-Indikatoren.CandleType– Kerzen-Zeitrahmen für Berechnungen.BuyOpen– Long-Positionen eröffnen erlauben.SellOpen– Short-Positionen eröffnen erlauben.BuyClose– Long-Positionen schließen erlauben.SellClose– Short-Positionen schließen erlauben.
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>Five weighted Momentum indicators, two OSMA smoothing stages and the documented t-2/t-3 signal.</summary>
public class ColorZerolagMomentumOsmaStrategy : Strategy
{
private readonly StrategyParam<int>[] _periods = new StrategyParam<int>[5];
private readonly StrategyParam<decimal>[] _factors = new StrategyParam<decimal>[5];
private readonly StrategyParam<int> _smoothing1, _smoothing2;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _buyOpen, _sellOpen, _buyClose, _sellClose;
private readonly Queue<decimal> _closes = new();
private decimal? _slow, _smoothed, _last, _prev2, _prev3;
private Order _pending;
public int MomentumPeriod1 { get => _periods[0].Value; set => _periods[0].Value = value; }
public int MomentumPeriod2 { get => _periods[1].Value; set => _periods[1].Value = value; }
public int MomentumPeriod3 { get => _periods[2].Value; set => _periods[2].Value = value; }
public int MomentumPeriod4 { get => _periods[3].Value; set => _periods[3].Value = value; }
public int MomentumPeriod5 { 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 int Smoothing1 { get => _smoothing1.Value; set => _smoothing1.Value = value; }
public int Smoothing2 { get => _smoothing2.Value; set => _smoothing2.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }
public ColorZerolagMomentumOsmaStrategy()
{
var periods = new[] { 5, 8, 13, 21, 34 };
for (var i = 0; i < 5; i++)
{
_periods[i] = Param("MomentumPeriod" + (i + 1), periods[i]).SetGreaterThanZero()
.SetDisplay("Momentum period " + (i + 1), "Price difference lookback", "Indicator");
_factors[i] = Param("Factor" + (i + 1), 1m)
.SetDisplay("Factor " + (i + 1), "Component weight", "Indicator");
}
_smoothing1 = Param(nameof(Smoothing1), 9).SetGreaterThanZero()
.SetDisplay("Smoothing 1", "Slow trend EMA", "Indicator");
_smoothing2 = Param(nameof(Smoothing2), 5).SetGreaterThanZero()
.SetDisplay("Smoothing 2", "OSMA EMA", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Signal candles", "General");
_buyOpen = Param(nameof(BuyOpen), true).SetDisplay("Buy Open", "Enable long entries", "Trading");
_sellOpen = Param(nameof(SellOpen), true).SetDisplay("Sell Open", "Enable short entries", "Trading");
_buyClose = Param(nameof(BuyClose), true).SetDisplay("Buy Close", "Enable long exits", "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()
{
_closes.Clear();
_slow = _smoothed = _last = _prev2 = _prev3 = null;
_pending = null;
}
protected override void OnReseted()
{
base.OnReseted();
ResetHistory();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetHistory();
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var max = _periods.Max(p => p.Value);
_closes.Enqueue(candle.ClosePrice);
if (_closes.Count > max + 1)
_closes.Dequeue();
if (_closes.Count < max + 1)
return;
var closes = _closes.ToArray();
var fast = 0m;
for (var i = 0; i < 5; i++)
{
// Matches StockSharp Momentum: close[t] - close[t - period].
var period = _periods[i].Value;
fast += (candle.ClosePrice - closes[closes.Length - period - 1]) * _factors[i].Value;
}
var slow = _slow is decimal oldSlow
? oldSlow + 2m / (Smoothing1 + 1m) * (fast - oldSlow)
: fast;
var rawOsma = fast - slow;
var osma = _smoothed is decimal oldOsma
? oldOsma + 2m / (Smoothing2 + 1m) * (rawOsma - oldOsma)
: rawOsma;
// The README refers to t-2 against t-3, then t against t-2.
var buy = _prev3 is decimal third && _prev2 is decimal second
&& second < third && osma > second;
var sell = _prev3 is decimal thirdDown && _prev2 is decimal secondDown
&& secondDown > thirdDown && osma < secondDown;
_prev3 = _prev2;
_prev2 = _last;
_last = _smoothed = osma;
_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
from StockSharp.Algo.Strategies import Strategy
class color_zerolag_momentum_osma_strategy(Strategy):
"""Weighted five momentum calculations with the documented t-2/t-3 OSMA turning signal."""
def __init__(self):
super(color_zerolag_momentum_osma_strategy, self).__init__()
self._periods = []
self._factors = []
for index, period in enumerate((5, 8, 13, 21, 34), start=1):
self._periods.append(self.Param("MomentumPeriod" + str(index), period).SetGreaterThanZero()
.SetDisplay("Momentum Period " + str(index), "Price difference lookback", "Indicator"))
self._factors.append(self.Param("Factor" + str(index), 1.0)
.SetDisplay("Factor " + str(index), "Component weight", "Indicator"))
self._smoothing1 = self.Param("Smoothing1", 9).SetGreaterThanZero() \
.SetDisplay("Smoothing 1", "Slow trend EMA", "Indicator")
self._smoothing2 = self.Param("Smoothing2", 5).SetGreaterThanZero() \
.SetDisplay("Smoothing 2", "OSMA EMA", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Signal candles", "General")
self._buy_open = self.Param("BuyOpen", True).SetDisplay("Buy Open", "Enable long entries", "Trading")
self._sell_open = self.Param("SellOpen", True).SetDisplay("Sell Open", "Enable short entries", "Trading")
self._buy_close = self.Param("BuyClose", True).SetDisplay("Buy Close", "Enable long exits", "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._closes = []
self._slow = self._smoothed = self._last = self._prev2 = self._prev3 = None
self._pending = None
def OnReseted(self):
super(color_zerolag_momentum_osma_strategy, self).OnReseted()
self._reset_history()
def OnStarted2(self, time):
super(color_zerolag_momentum_osma_strategy, self).OnStarted2(time)
self._reset_history()
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
max_period = max(int(p.Value) for p in self._periods)
close = float(candle.ClosePrice)
self._closes.append(close)
if len(self._closes) > max_period + 1:
self._closes.pop(0)
if len(self._closes) < max_period + 1:
return
fast = 0.0
for param, factor in zip(self._periods, self._factors):
period = int(param.Value)
fast += (close - self._closes[-period-1]) * float(factor.Value)
slow = fast if self._slow is None else self._slow + 2.0 / (int(self._smoothing1.Value) + 1.0) * (fast - self._slow)
raw_osma = fast - slow
osma = raw_osma if self._smoothed is None else self._smoothed + 2.0 / (int(self._smoothing2.Value) + 1.0) * (raw_osma - self._smoothed)
buy = self._prev3 is not None and self._prev2 < self._prev3 and osma > self._prev2
sell = self._prev3 is not None and self._prev2 > self._prev3 and osma < self._prev2
self._prev3, self._prev2, self._last = self._prev2, self._last, osma
self._smoothed, self._slow = osma, 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_momentum_osma_strategy()