Стратегия Color Zerolag X10 MA
Стратегия является упрощённым портом примера Exp_ColorZerolagX10MA.mq5 из MetaTrader. Используется индикатор zero lag EMA для обнаружения смены наклона. Когда средняя начинает расти после двух свечей снижения, открывается или переворачивается длинная позиция. Если средняя начинает падать после роста, открывается короткая позиция.
Исходный код опирался на сложный индикатор из десяти сглаженных скользящих средних. Здесь он заменён встроенным ZeroLagExponentialMovingAverage из StockSharp. Стратегия работает на выбранном таймфрейме свечей, позволяя включать и отключать отдельные действия (открытие/закрытие длинных и коротких позиций).
Детали
- Условия входа:
- Long:
ZLEMA[t-2] > ZLEMA[t-1] и ZLEMA[t] > ZLEMA[t-1].
- Short:
ZLEMA[t-2] < ZLEMA[t-1] и ZLEMA[t] < ZLEMA[t-1].
- Направление: обе стороны.
- Условия выхода:
- Long закрывается при появлении сигнала на Short и включённом
BuyPosClose.
- Short закрывается при появлении сигнала на Long и включённом
SellPosClose.
- Стопы: не используются, выход по противоположному сигналу.
- Значения по умолчанию:
Length = 20.
CandleType = 4-часовой таймфрейм.
- Все флаги действий (
BuyPosOpen, SellPosOpen, BuyPosClose, SellPosClose) включены.
- Фильтры:
- Категория: Следование тренду
- Направление: Обе
- Индикаторы: Один
- Стопы: Нет
- Сложность: Простая
- Таймфрейм: Среднесрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Trades ZLEMA slope reversals with independent long/short entry/exit switches.
/// </summary>
public class ColorZerolagX10MaStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _openBuy;
private readonly StrategyParam<bool> _openSell;
private readonly StrategyParam<bool> _closeBuy;
private readonly StrategyParam<bool> _closeSell;
private decimal? _previous;
private decimal? _beforePrevious;
private Order _pendingOrder;
public int Length { get => _length.Value; set => _length.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public bool BuyPosOpen { get => _openBuy.Value; set => _openBuy.Value = value; }
public bool SellPosOpen { get => _openSell.Value; set => _openSell.Value = value; }
public bool BuyPosClose { get => _closeBuy.Value; set => _closeBuy.Value = value; }
public bool SellPosClose { get => _closeSell.Value; set => _closeSell.Value = value; }
public ColorZerolagX10MaStrategy()
{
_length = Param(nameof(Length), 20).SetGreaterThanZero()
.SetDisplay("Length", "ZLEMA length", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_openBuy = Param(nameof(BuyPosOpen), true).SetDisplay("Open Buy", "Allow long entries", "Trading");
_openSell = Param(nameof(SellPosOpen), true).SetDisplay("Open Sell", "Allow short entries", "Trading");
_closeBuy = Param(nameof(BuyPosClose), true).SetDisplay("Close Buy", "Allow long exits", "Trading");
_closeSell = Param(nameof(SellPosClose), true).SetDisplay("Close Sell", "Allow short exits", "Trading");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_previous = _beforePrevious = null;
_pendingOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previous = _beforePrevious = null;
_pendingOrder = null;
var zlma = new ZeroLagExponentialMovingAverage { Length = Length };
SubscribeCandles(CandleType).Bind(zlma, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal current)
{
if (candle.State != CandleStates.Finished)
return;
var turnUp = _beforePrevious is decimal older && _previous is decimal previous
&& previous < older && current > previous;
var turnDown = _beforePrevious is decimal olderDown && _previous is decimal previousDown
&& previousDown > olderDown && current < previousDown;
_beforePrevious = _previous;
_previous = current;
if (!IsFormedAndOnlineAndAllowTrading()
|| (_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed)))
return;
if (turnUp)
{
if (Position < 0m && SellPosClose)
_pendingOrder = BuyMarket(-Position + (BuyPosOpen ? Volume : 0m));
else if (Position == 0m && BuyPosOpen)
_pendingOrder = BuyMarket(Volume);
}
else if (turnDown)
{
if (Position > 0m && BuyPosClose)
_pendingOrder = SellMarket(Position + (SellPosOpen ? Volume : 0m));
else if (Position == 0m && SellPosOpen)
_pendingOrder = SellMarket(Volume);
}
}
}
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, OrderStates
from StockSharp.Algo.Indicators import ZeroLagExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class color_zerolag_x10_ma_strategy(Strategy):
"""ZLEMA slope reversals with independent entry and exit permissions."""
def __init__(self):
super(color_zerolag_x10_ma_strategy, self).__init__()
self._length = self.Param("Length", 20).SetDisplay("Length", "ZLEMA length", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._open_buy = self.Param("BuyPosOpen", True).SetDisplay("Open Buy", "Allow long entries", "Trading")
self._open_sell = self.Param("SellPosOpen", True).SetDisplay("Open Sell", "Allow short entries", "Trading")
self._close_buy = self.Param("BuyPosClose", True).SetDisplay("Close Buy", "Allow long exits", "Trading")
self._close_sell = self.Param("SellPosClose", True).SetDisplay("Close Sell", "Allow short exits", "Trading")
self._previous = None
self._before_previous = None
self._pending_order = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(color_zerolag_x10_ma_strategy, self).OnReseted()
self._previous = self._before_previous = None
self._pending_order = None
def OnStarted2(self, time):
super(color_zerolag_x10_ma_strategy, self).OnStarted2(time)
self._previous = self._before_previous = None
self._pending_order = None
zlma = ZeroLagExponentialMovingAverage()
zlma.Length = self._length.Value
self.SubscribeCandles(self.CandleType).Bind(zlma, self.on_process).Start()
def on_process(self, candle, value):
if candle.State != CandleStates.Finished:
return
current = float(value)
up = self._before_previous is not None and self._previous < self._before_previous and current > self._previous
down = self._before_previous is not None and self._previous > self._before_previous and current < self._previous
self._before_previous, self._previous = self._previous, current
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
return
if up:
if self.Position < 0 and self._close_sell.Value:
amount = -self.Position + (self.Volume if self._open_buy.Value else 0)
self._pending_order = self.BuyMarket(amount)
elif self.Position == 0 and self._open_buy.Value:
self._pending_order = self.BuyMarket(self.Volume)
elif down:
if self.Position > 0 and self._close_buy.Value:
amount = self.Position + (self.Volume if self._open_sell.Value else 0)
self._pending_order = self.SellMarket(amount)
elif self.Position == 0 and self._open_sell.Value:
self._pending_order = self.SellMarket(self.Volume)
def CreateClone(self):
return color_zerolag_x10_ma_strategy()