IU EMA Channel
Конвертация из скрипта TradingView "IU EMA Channel Strategy". Стратегия торгует при пересечении цены с EMA, построенными по максимумам и минимумам. Стоп ставится на экстремум предыдущей свечи, тейк-профит рассчитывается по соотношению риск/прибыль.
Детали
- Критерии входа: Закрытие пересекает EMA по максимумам для лонга, EMA по минимумам для шорта.
- Длинные/короткие: Обе стороны.
- Критерии выхода: Стоп на предыдущем экстремуме или тейк по соотношению риск/прибыль.
- Стопы: Да, фиксированные стоп и цель.
- Значения по умолчанию:
EmaLength= 100RiskToReward= 2CandleType= TimeSpan.FromMinutes(1)
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: EMA
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Переменный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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;
/// Strategy based on price crossing EMA channels. Entries use crossovers of close with high/low EMAs. Exits apply stop-loss at previous candle extreme and take-profit based on risk-to-reward ratio.
public class IuEmaChannelStrategy : Strategy
{
private StrategyParam<int> _emaLength;
private StrategyParam<decimal> _riskToReward;
private StrategyParam<int> _maxEntries;
private StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevHighEma;
private decimal _prevLowEma;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _stopPrice;
private decimal _takePrice;
private bool _isInitialized;
private int _entriesExecuted;
private bool _entryPending;
private bool _exitPending;
public IuEmaChannelStrategy()
{
_emaLength = Param(nameof(EmaLength), 100)
.SetDisplay("EMA Length", "EMA period", "General")
.SetGreaterThanZero()
;
_riskToReward = Param(nameof(RiskToReward), 2m)
.SetDisplay("Risk To Reward", "Reward to risk ratio", "General")
.SetGreaterThanZero()
;
_maxEntries = Param(nameof(MaxEntries), 35)
.SetDisplay("Max Entries", "Maximum number of entries per test run", "General")
.SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public decimal RiskToReward { get => _riskToReward.Value; set => _riskToReward.Value = value; }
public int MaxEntries { get => _maxEntries.Value; set => _maxEntries.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_isInitialized = false;
_prevClose = 0m;
_prevHighEma = 0m;
_prevLowEma = 0m;
_prevHigh = 0m;
_prevLow = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_entriesExecuted = 0;
_entryPending = false;
_exitPending = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highEma = new EMA
{
Length = EmaLength
};
var lowEma = new EMA
{
Length = EmaLength
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highEma, lowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highEma);
DrawIndicator(area, lowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highEmaValue, decimal lowEmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isInitialized)
{
_prevClose = candle.ClosePrice;
_prevHighEma = highEmaValue;
_prevLowEma = lowEmaValue;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_isInitialized = true;
return;
}
if (Position == 0)
{
_exitPending = false;
_entryPending = false;
}
else
{
_entryPending = false;
}
if (Position == 0)
{
if (_entriesExecuted >= MaxEntries || _entryPending)
{
_prevClose = candle.ClosePrice;
_prevHighEma = highEmaValue;
_prevLowEma = lowEmaValue;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
return;
}
var crossUp = _prevClose <= _prevHighEma && candle.ClosePrice > highEmaValue;
var crossDown = _prevClose >= _prevLowEma && candle.ClosePrice < lowEmaValue;
if (crossUp)
{
_stopPrice = _prevLow;
_takePrice = candle.ClosePrice + (candle.ClosePrice - _stopPrice) * RiskToReward;
BuyMarket();
_entriesExecuted++;
_entryPending = true;
}
else if (crossDown)
{
_stopPrice = _prevHigh;
_takePrice = candle.ClosePrice - (_stopPrice - candle.ClosePrice) * RiskToReward;
SellMarket();
_entriesExecuted++;
_entryPending = true;
}
}
else if (Position > 0)
{
if (!_exitPending && (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice))
{
SellMarket();
_exitPending = true;
}
}
else if (Position < 0)
{
if (!_exitPending && (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice))
{
BuyMarket();
_exitPending = true;
}
}
_prevClose = candle.ClosePrice;
_prevHighEma = highEmaValue;
_prevLowEma = lowEmaValue;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class iu_ema_channel_strategy(Strategy):
def __init__(self):
super(iu_ema_channel_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 100) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "EMA period", "General")
self._risk_to_reward = self.Param("RiskToReward", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Risk To Reward", "Reward to risk ratio", "General")
self._max_entries = self.Param("MaxEntries", 35) \
.SetGreaterThanZero() \
.SetDisplay("Max Entries", "Maximum number of entries per test run", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._prev_close = 0.0
self._prev_high_ema = 0.0
self._prev_low_ema = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._is_initialized = False
self._entries_executed = 0
self._entry_pending = False
self._exit_pending = False
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(iu_ema_channel_strategy, self).OnReseted()
self._is_initialized = False
self._prev_close = 0.0
self._prev_high_ema = 0.0
self._prev_low_ema = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._entries_executed = 0
self._entry_pending = False
self._exit_pending = False
def OnStarted2(self, time):
super(iu_ema_channel_strategy, self).OnStarted2(time)
high_ema = ExponentialMovingAverage()
high_ema.Length = self._ema_length.Value
low_ema = ExponentialMovingAverage()
low_ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(high_ema, low_ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, high_ema)
self.DrawIndicator(area, low_ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, high_ema_val, low_ema_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
h_ema = float(high_ema_val)
l_ema = float(low_ema_val)
rr = float(self._risk_to_reward.Value)
if not self._is_initialized:
self._prev_close = close
self._prev_high_ema = h_ema
self._prev_low_ema = l_ema
self._prev_high = high
self._prev_low = low
self._is_initialized = True
return
if self.Position == 0:
self._exit_pending = False
self._entry_pending = False
else:
self._entry_pending = False
if self.Position == 0:
if self._entries_executed >= self._max_entries.Value or self._entry_pending:
self._prev_close = close
self._prev_high_ema = h_ema
self._prev_low_ema = l_ema
self._prev_high = high
self._prev_low = low
return
cross_up = self._prev_close <= self._prev_high_ema and close > h_ema
cross_down = self._prev_close >= self._prev_low_ema and close < l_ema
if cross_up:
self._stop_price = self._prev_low
self._take_price = close + (close - self._stop_price) * rr
self.BuyMarket()
self._entries_executed += 1
self._entry_pending = True
elif cross_down:
self._stop_price = self._prev_high
self._take_price = close - (self._stop_price - close) * rr
self.SellMarket()
self._entries_executed += 1
self._entry_pending = True
elif self.Position > 0:
if not self._exit_pending and (low <= self._stop_price or high >= self._take_price):
self.SellMarket()
self._exit_pending = True
elif self.Position < 0:
if not self._exit_pending and (high >= self._stop_price or low <= self._take_price):
self.BuyMarket()
self._exit_pending = True
self._prev_close = close
self._prev_high_ema = h_ema
self._prev_low_ema = l_ema
self._prev_high = high
self._prev_low = low
def CreateClone(self):
return iu_ema_channel_strategy()