Estratégia Color Zerolag Momentum OSMA
Esta estratégia constrói um oscilador OSMA de Momentum de zero defasagem personalizado usando cinco cálculos de Momentum. Quando o valor do oscilador de dois períodos atrás está abaixo do valor de três períodos atrás, a tendência é considerada ascendente. Neste caso, as posições vendidas são fechadas e uma nova posição comprada pode ser aberta se o valor mais recente estiver acima do valor de dois períodos atrás. Quando o valor de dois períodos atrás está acima do valor de três períodos atrás, a tendência é descendente, as posições compradas são fechadas e uma vendida pode ser aberta se o último valor estiver abaixo do valor de dois períodos atrás.
Parâmetros
Smoothing1– primeiro fator de suavização para a tendência lenta.Smoothing2– segundo fator de suavização para a linha OSMA.Factor1-5– pesos aplicados a cada componente de Momentum.MomentumPeriod1-5– períodos para os indicadores de Momentum.CandleType– período de candles para cálculos.BuyOpen– permitir abertura de posições compradas.SellOpen– permitir abertura de posições vendidas.BuyClose– permitir fechamento de posições compradas.SellClose– permitir fechamento de posições vendidas.
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()