Exp Sinewave2 X2 — мультифреймовая трендовая стратегия, основанная на идеях Джона Элерса. Старший таймфрейм задаёт направление, младший отвечает за точку входа и своевременный выход. Все расчёты выполняются переписанным индикатором Sinewave2, который включает адаптивный модуль CyclePeriod для сохранения совместимости с оригиналом на MQL.
Используемые индикаторы
Sinewave2 на старшем таймфрейме — сравнение линии Lead и основной линии определяет доминирующий тренд.
Sinewave2 на младшем таймфрейме — фиксирует последние пересечения и генерирует торговые сигналы в сторону тренда.
Логика работы
Фильтр тренда
Рассчитать Sinewave2 на старшем таймфрейме.
Взять значения линий на SignalBarHigh баров назад.
Если Lead > Sine — тренд бычий, если Lead < Sine — тренд медвежий, иначе нейтральный.
Сигналы входа
Дождаться закрытия бара младшего таймфрейма.
Прочитать значения Lead и Sine на смещениях SignalBarLow и SignalBarLow + 1.
Лонг: предыдущее пересечение было сверху вниз, текущий бар возвращает Lead ниже Sine, тренд старшего таймфрейма бычий и включён EnableBuyOpen.
Шорт: предыдущее пересечение было снизу вверх, текущий бар выводит Lead выше Sine, тренд старшего таймфрейма медвежий и включён EnableSellOpen.
Выходы
Флаги EnableBuyCloseLower / EnableSellCloseLower закрывают позиции при обратном пересечении на младшем таймфрейме.
Флаги EnableBuyCloseTrend / EnableSellCloseTrend закрывают позицию сразу после смены тренда на старшем таймфрейме.
Стоп-лосс и тейк-профит проверяются по максимуму/минимуму бара с учётом расстояний StopLossPoints и TakeProfitPoints в шагах цены.
Управление рисками
При реверсе объём заявки равен Volume + |Position|, что одновременно закрывает текущую позицию и открывает новую.
После входа вызывается SetRiskLevels, который конвертирует точечные параметры в абсолютные цены через Security.PriceStep (если шаг цены неизвестен, используется 1).
Параметры
Название
Описание
AlphaHigh
Коэффициент сглаживания Sinewave2 на старшем таймфрейме.
AlphaLow
Коэффициент сглаживания Sinewave2 на младшем таймфрейме.
SignalBarHigh
Число баров для оценки тренда на старшем таймфрейме.
SignalBarLow
Число баров для анализа пересечений на младшем таймфрейме.
EnableBuyOpen / EnableSellOpen
Разрешение на открытие длинных/коротких позиций.
EnableBuyCloseTrend / EnableSellCloseTrend
Принудительное закрытие при смене тренда старшего таймфрейма.
EnableBuyCloseLower / EnableSellCloseLower
Закрытие при обратном пересечении на младшем таймфрейме.
StopLossPoints
Дистанция стоп-лосса в шагах цены.
TakeProfitPoints
Дистанция тейк-профита в шагах цены.
HigherCandleType / LowerCandleType
Типы данных (таймфреймы) для фильтра и сигналов.
Примечания
Стратегия работает только с закрытыми барами и игнорирует незавершённые свечи.
Индикаторы Sinewave2 и CyclePeriod реализованы по классической методике Элерса, что облегчает сопоставление результатов с кодом MQL.
При совпадении таймфреймов старшего и младшего уровней используется одна подписка на свечи, чтобы не перегружать канал данных.
Объём сделок задаётся свойством Volume базового класса Strategy и должен быть настроен перед запуском.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ehlers Sinewave X2 strategy (simplified). Uses CCI oscillator for entries.
/// </summary>
public class ExpSinewave2X2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciLength;
private readonly StrategyParam<int> _emaLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CciLength
{
get => _cciLength.Value;
set => _cciLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public ExpSinewave2X2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_cciLength = Param(nameof(CciLength), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Length", "CCI period", "Indicators");
_emaLength = Param(nameof(EmaLength), 30)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Trend filter EMA", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var cci = new CommodityChannelIndex { Length = CciLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
decimal prevCci = 0;
bool hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, ema, (ICandleMessage candle, decimal cciValue, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevCci = cciValue;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevCci = cciValue;
return;
}
// CCI crosses above -100 with price above EMA
if (prevCci < -100 && cciValue >= -100 && candle.ClosePrice > emaValue && Position <= 0)
{
BuyMarket();
}
// CCI crosses below 100 with price below EMA
else if (prevCci > 100 && cciValue <= 100 && candle.ClosePrice < emaValue && Position >= 0)
{
SellMarket();
}
prevCci = cciValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 CommodityChannelIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_sinewave2_x2_strategy(Strategy):
"""
Ehlers Sinewave X2 strategy (simplified). Uses CCI oscillator for entries.
CCI crosses above -100 with price above EMA to buy, CCI crosses below 100 with price below EMA to sell.
"""
def __init__(self):
super(exp_sinewave2_x2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candles", "General")
self._cci_length = self.Param("CciLength", 14) \
.SetDisplay("CCI Length", "CCI period", "Indicators")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend filter EMA", "Indicators")
self._prev_cci = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_sinewave2_x2_strategy, self).OnReseted()
self._prev_cci = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(exp_sinewave2_x2_strategy, self).OnStarted2(time)
self._has_prev = False
cci = CommodityChannelIndex()
cci.Length = self._cci_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, cci_val, ema_val):
if candle.State != CandleStates.Finished:
return
cci_val = float(cci_val)
ema_val = float(ema_val)
if not self._has_prev:
self._prev_cci = cci_val
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_cci = cci_val
return
close = float(candle.ClosePrice)
if self._prev_cci < -100 and cci_val >= -100 and close > ema_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_cci > 100 and cci_val <= 100 and close < ema_val and self.Position >= 0:
self.SellMarket()
self._prev_cci = cci_val
def CreateClone(self):
return exp_sinewave2_x2_strategy()