Стратегия Sidus
Эта стратегия реализует систему скользящих средних SIDUS. Она торгует по пересечениям двух линейно-взвешенных средних с подтверждением экспоненциальной средней. Позиция открывается, когда быстрая LWMA пересекает медленную LWMA или когда медленная LWMA пересекает медленную EMA. Обратные пересечения закрывают или переворачивают позицию. Проценты стоп-лосса и тейк-профита управляют риском.
Тестирование показывает среднюю годовую доходность около 25%. Лучше всего работает на валютных парах.
Основная идея — ловить смену тренда, когда быстрые и медленные средние выстраиваются. Пара LWMA быстро реагирует на цену, а более медленная EMA фильтрует шум. При появлении бычьего или медвежьего сигнала стратегия входит в соответствующем направлении и использует защитные уровни для выхода при неблагоприятном движении.
Детали
- Условия входа:
- Long: быстрая LWMA пересекает медленную LWMA снизу вверх или медленная LWMA пересекает медленную EMA снизу вверх.
- Short: быстрая LWMA пересекает медленную LWMA сверху вниз или медленная LWMA пересекает медленную EMA сверху вниз.
- Покупка/Продажа: обе стороны.
- Условия выхода:
- Обратное пересечение или срабатывание защитных уровней.
- Стопы: Да, используются процентные тейк-профит и стоп-лосс через
StartProtection.
- Значения по умолчанию:
- Период быстрой EMA = 18.
- Период медленной EMA = 28.
- Период быстрой LWMA = 5.
- Период медленной LWMA = 8.
- Тейк-профит = 2%.
- Стоп-лосс = 1%.
- Фильтры: нет.
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()