Simple MA ADX EA
Стратегия, которая сочетает EMA и индекс направленного движения (ADX) для подтверждения тренда.
Покупка осуществляется при росте EMA, закрытии предыдущей свечи выше EMA, значении ADX выше порога и +DI больше -DI. Продажа выполняется при обратных условиях. Управление риском реализовано через стоп-лосс и тейк-профит.
Детали
- Условия входа: направление EMA, цена относительно EMA, ADX, +DI/-DI.
- Длинные/короткие: в обе стороны.
- Условия выхода: противоположный сигнал или защитные ордера.
- Стопы: да.
- Значения по умолчанию:
AdxPeriod= 8MaPeriod= 8AdxThreshold= 22mStopLoss= 30mTakeProfit= 100mVolume= 0.1mCandleType= TimeSpan.FromMinutes(1)
- Фильтры:
- Категория: Тренд
- Направление: Оба
- Индикаторы: EMA, ADX
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Внутридневной (1м)
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining EMA direction with a slower trend filter.
/// </summary>
public class SimpleMaAdxEaStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema;
private ExponentialMovingAverage _trendMa;
private decimal _emaPrev1;
private decimal _emaPrev2;
private decimal _trendPrev;
private decimal _prevClose;
private bool _isInitialized;
private int _barsSinceTrade;
/// <summary>
/// Trend filter period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// EMA period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Minimum percentage distance between the fast and slow averages.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SimpleMaAdxEaStrategy"/> class.
/// </summary>
public SimpleMaAdxEaStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 21)
.SetDisplay("Trend Period", "Period for trend confirmation", "Indicators");
_maPeriod = Param(nameof(MaPeriod), 8)
.SetDisplay("MA Period", "EMA calculation period", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 0.05m)
.SetDisplay("Trend Threshold", "Minimum average distance in percent", "Indicators");
_stopLoss = Param(nameof(StopLoss), 400m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk Management");
_takeProfit = Param(nameof(TakeProfit), 1200m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk Management");
_cooldownBars = Param(nameof(CooldownBars), 200)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_trendMa = null;
_emaPrev1 = 0m;
_emaPrev2 = 0m;
_trendPrev = 0m;
_prevClose = 0m;
_isInitialized = false;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = MaPeriod };
_trendMa = new ExponentialMovingAverage { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
stopLoss: new Unit(StopLoss, UnitTypes.Absolute),
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
useMarketOrders: true);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var ema = _ema.Process(new DecimalIndicatorValue(_ema, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var trendMa = _trendMa.Process(new DecimalIndicatorValue(_trendMa, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_ema.IsFormed || !_trendMa.IsFormed || trendMa == 0m)
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
if (!_isInitialized)
{
_emaPrev2 = ema;
_emaPrev1 = ema;
_trendPrev = trendMa;
_prevClose = candle.ClosePrice;
_isInitialized = true;
return;
}
var distancePercent = Math.Abs(ema - trendMa) / trendMa * 100m;
var buyCond1 = ema > _emaPrev1 && _emaPrev1 >= _emaPrev2;
var buyCond2 = _prevClose > _emaPrev1 && candle.ClosePrice > trendMa;
var buyCond3 = trendMa >= _trendPrev && distancePercent >= AdxThreshold;
var sellCond1 = ema < _emaPrev1 && _emaPrev1 <= _emaPrev2;
var sellCond2 = _prevClose < _emaPrev1 && candle.ClosePrice < trendMa;
var sellCond3 = trendMa <= _trendPrev && distancePercent >= AdxThreshold;
if (_barsSinceTrade >= CooldownBars && Position == 0)
{
if (buyCond1 && buyCond2 && buyCond3)
{
BuyMarket();
_barsSinceTrade = 0;
}
else if (sellCond1 && sellCond2 && sellCond3)
{
SellMarket();
_barsSinceTrade = 0;
}
}
_emaPrev2 = _emaPrev1;
_emaPrev1 = ema;
_trendPrev = trendMa;
_prevClose = candle.ClosePrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class simple_ma_adx_ea_strategy(Strategy):
def __init__(self):
super(simple_ma_adx_ea_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 21) \
.SetDisplay("Trend Period", "Period for trend confirmation", "Indicators")
self._ma_period = self.Param("MaPeriod", 8) \
.SetDisplay("MA Period", "EMA calculation period", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 0.05) \
.SetDisplay("Trend Threshold", "Minimum average distance in percent", "Indicators")
self._stop_loss = self.Param("StopLoss", 400.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk Management")
self._take_profit = self.Param("TakeProfit", 1200.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk Management")
self._cooldown_bars = self.Param("CooldownBars", 200) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._ema = None
self._trend_ma = None
self._ema_prev1 = 0.0
self._ema_prev2 = 0.0
self._trend_prev = 0.0
self._prev_close = 0.0
self._is_initialized = False
self._bars_since_trade = 0
@property
def AdxPeriod(self):
return self._adx_period.Value
@AdxPeriod.setter
def AdxPeriod(self, value):
self._adx_period.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
@property
def AdxThreshold(self):
return self._adx_threshold.Value
@AdxThreshold.setter
def AdxThreshold(self, value):
self._adx_threshold.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(simple_ma_adx_ea_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.MaPeriod
self._trend_ma = ExponentialMovingAverage()
self._trend_ma.Length = self.AdxPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute),
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute),
useMarketOrders=True)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
ema_val = float(process_float(self._ema, close, candle.OpenTime, True))
trend_ma_val = float(process_float(self._trend_ma, close, candle.OpenTime, True))
if not self._ema.IsFormed or not self._trend_ma.IsFormed or trend_ma_val == 0.0:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
if not self._is_initialized:
self._ema_prev2 = ema_val
self._ema_prev1 = ema_val
self._trend_prev = trend_ma_val
self._prev_close = float(close)
self._is_initialized = True
return
distance_percent = abs(ema_val - trend_ma_val) / trend_ma_val * 100.0
buy_cond1 = ema_val > self._ema_prev1 and self._ema_prev1 >= self._ema_prev2
buy_cond2 = self._prev_close > self._ema_prev1 and float(close) > trend_ma_val
buy_cond3 = trend_ma_val >= self._trend_prev and distance_percent >= float(self.AdxThreshold)
sell_cond1 = ema_val < self._ema_prev1 and self._ema_prev1 <= self._ema_prev2
sell_cond2 = self._prev_close < self._ema_prev1 and float(close) < trend_ma_val
sell_cond3 = trend_ma_val <= self._trend_prev and distance_percent >= float(self.AdxThreshold)
if self._bars_since_trade >= self.CooldownBars and self.Position == 0:
if buy_cond1 and buy_cond2 and buy_cond3:
self.BuyMarket()
self._bars_since_trade = 0
elif sell_cond1 and sell_cond2 and sell_cond3:
self.SellMarket()
self._bars_since_trade = 0
self._ema_prev2 = self._ema_prev1
self._ema_prev1 = ema_val
self._trend_prev = trend_ma_val
self._prev_close = float(close)
def OnReseted(self):
super(simple_ma_adx_ea_strategy, self).OnReseted()
self._ema = None
self._trend_ma = None
self._ema_prev1 = 0.0
self._ema_prev2 = 0.0
self._trend_prev = 0.0
self._prev_close = 0.0
self._is_initialized = False
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return simple_ma_adx_ea_strategy()