Sidus Strategy
This strategy implements the SIDUS moving average system. It trades using crossovers between two linear weighted moving averages and a confirming exponential average. A position is opened when the short-term LWMA crosses the long-term LWMA or when the long LWMA crosses the slow EMA. Opposite crossovers close or reverse the position. A percentage based stop-loss and take-profit manage risk.
Testing indicates an average annual return of about 25%. It performs best on forex pairs.
The core idea is to capture trend shifts when the fast and slow moving averages realign. The LWMA pair reacts quickly to price changes while the slower EMA filters noise. When either bullish or bearish alignment occurs, the strategy enters in that direction and relies on the protection levels to exit during adverse moves.
Details
- Entry Criteria:
- Long: fast LWMA crosses above slow LWMA or slow LWMA crosses above slow EMA.
- Short: fast LWMA crosses below slow LWMA or slow LWMA crosses below slow EMA.
- Long/Short: Both.
- Exit Criteria:
- Opposite crossover or protective stop levels.
- Stops: Yes, uses percentage-based take-profit and stop-loss via
StartProtection. - Default Values:
- Fast EMA length = 18.
- Slow EMA length = 28.
- Fast LWMA length = 5.
- Slow LWMA length = 8.
- Take profit = 2%.
- Stop loss = 1%.
- Filters: None.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SIDUS strategy based on moving average crossovers.
/// Buys when fast LWMA crosses above slow LWMA or when slow LWMA crosses above slow EMA.
/// Sells on opposite crossovers.
/// </summary>
public class SidusStrategy : Strategy
{
private readonly StrategyParam<int> _fastEma;
private readonly StrategyParam<int> _slowEma;
private readonly StrategyParam<int> _fastLwma;
private readonly StrategyParam<int> _slowLwma;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _fastEmaIndicator;
private ExponentialMovingAverage _slowEmaIndicator;
private WeightedMovingAverage _fastLwmaIndicator;
private WeightedMovingAverage _slowLwmaIndicator;
private decimal _prevFastLwma;
private decimal _prevSlowLwma;
private decimal _prevSlowEma;
private bool _isInitialized;
public int FastEma { get => _fastEma.Value; set => _fastEma.Value = value; }
public int SlowEma { get => _slowEma.Value; set => _slowEma.Value = value; }
public int FastLwma { get => _fastLwma.Value; set => _fastLwma.Value = value; }
public int SlowLwma { get => _slowLwma.Value; set => _slowLwma.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public SidusStrategy()
{
_fastEma = Param(nameof(FastEma), 18)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Length of the fast EMA", "Sidus")
.SetOptimize(10, 30, 2);
_slowEma = Param(nameof(SlowEma), 28)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Length of the slow EMA", "Sidus")
.SetOptimize(20, 50, 2);
_fastLwma = Param(nameof(FastLwma), 5)
.SetGreaterThanZero()
.SetDisplay("Fast LWMA", "Length of the fast LWMA", "Sidus")
.SetOptimize(3, 10, 1);
_slowLwma = Param(nameof(SlowLwma), 8)
.SetGreaterThanZero()
.SetDisplay("Slow LWMA", "Length of the slow LWMA", "Sidus")
.SetOptimize(5, 15, 1);
_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();
_prevFastLwma = 0;
_prevSlowLwma = 0;
_prevSlowEma = 0;
_isInitialized = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEmaIndicator = new ExponentialMovingAverage { Length = FastEma };
_slowEmaIndicator = new ExponentialMovingAverage { Length = SlowEma };
_fastLwmaIndicator = new WeightedMovingAverage { Length = FastLwma };
_slowLwmaIndicator = new WeightedMovingAverage { Length = SlowLwma };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastEmaIndicator, _slowEmaIndicator, _fastLwmaIndicator, _slowLwmaIndicator, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastEmaIndicator);
DrawIndicator(area, _slowEmaIndicator);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastEmaValue, decimal slowEmaValue, decimal fastLwmaValue, decimal slowLwmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isInitialized)
{
_prevFastLwma = fastLwmaValue;
_prevSlowLwma = slowLwmaValue;
_prevSlowEma = slowEmaValue;
_isInitialized = true;
return;
}
var buySignal =
(fastLwmaValue > slowLwmaValue && _prevFastLwma <= _prevSlowLwma) ||
(slowLwmaValue > slowEmaValue && _prevSlowLwma <= _prevSlowEma);
var sellSignal =
(fastLwmaValue < slowLwmaValue && _prevFastLwma >= _prevSlowLwma) ||
(slowLwmaValue < slowEmaValue && _prevSlowLwma >= _prevSlowEma);
if (IsFormedAndOnlineAndAllowTrading())
{
if (buySignal && Position <= 0)
BuyMarket();
else if (sellSignal && Position >= 0)
SellMarket();
}
_prevFastLwma = fastLwmaValue;
_prevSlowLwma = slowLwmaValue;
_prevSlowEma = slowEmaValue;
}
}
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 ExponentialMovingAverage, WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class sidus_strategy(Strategy):
def __init__(self):
super(sidus_strategy, self).__init__()
self._fast_ema = self.Param("FastEma", 18) \
.SetDisplay("Fast EMA", "Length of the fast EMA", "Sidus")
self._slow_ema = self.Param("SlowEma", 28) \
.SetDisplay("Slow EMA", "Length of the slow EMA", "Sidus")
self._fast_lwma = self.Param("FastLwma", 5) \
.SetDisplay("Fast LWMA", "Length of the fast LWMA", "Sidus")
self._slow_lwma = self.Param("SlowLwma", 8) \
.SetDisplay("Slow LWMA", "Length of the slow LWMA", "Sidus")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast_lwma = 0.0
self._prev_slow_lwma = 0.0
self._prev_slow_ema = 0.0
self._is_initialized = False
@property
def fast_ema(self):
return self._fast_ema.Value
@property
def slow_ema(self):
return self._slow_ema.Value
@property
def fast_lwma(self):
return self._fast_lwma.Value
@property
def slow_lwma(self):
return self._slow_lwma.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(sidus_strategy, self).OnReseted()
self._prev_fast_lwma = 0.0
self._prev_slow_lwma = 0.0
self._prev_slow_ema = 0.0
self._is_initialized = False
def OnStarted2(self, time):
super(sidus_strategy, self).OnStarted2(time)
fast_ema_ind = ExponentialMovingAverage()
fast_ema_ind.Length = self.fast_ema
slow_ema_ind = ExponentialMovingAverage()
slow_ema_ind.Length = self.slow_ema
fast_lwma_ind = WeightedMovingAverage()
fast_lwma_ind.Length = self.fast_lwma
slow_lwma_ind = WeightedMovingAverage()
slow_lwma_ind.Length = self.slow_lwma
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema_ind, slow_ema_ind, fast_lwma_ind, slow_lwma_ind, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema_ind)
self.DrawIndicator(area, slow_ema_ind)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast_ema_val, slow_ema_val, fast_lwma_val, slow_lwma_val):
if candle.State != CandleStates.Finished:
return
fast_ema_val = float(fast_ema_val)
slow_ema_val = float(slow_ema_val)
fast_lwma_val = float(fast_lwma_val)
slow_lwma_val = float(slow_lwma_val)
if not self._is_initialized:
self._prev_fast_lwma = fast_lwma_val
self._prev_slow_lwma = slow_lwma_val
self._prev_slow_ema = slow_ema_val
self._is_initialized = True
return
buy_signal = (
(fast_lwma_val > slow_lwma_val and self._prev_fast_lwma <= self._prev_slow_lwma) or
(slow_lwma_val > slow_ema_val and self._prev_slow_lwma <= self._prev_slow_ema)
)
sell_signal = (
(fast_lwma_val < slow_lwma_val and self._prev_fast_lwma >= self._prev_slow_lwma) or
(slow_lwma_val < slow_ema_val and self._prev_slow_lwma >= self._prev_slow_ema)
)
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._prev_fast_lwma = fast_lwma_val
self._prev_slow_lwma = slow_lwma_val
self._prev_slow_ema = slow_ema_val
def CreateClone(self):
return sidus_strategy()