Long-Only MTF EMA Cloud
EMA cloud crossover strategy that trades long when the short EMA crosses above the long EMA. Uses fixed percentage stop loss and take profit.
Details
- Entry Criteria: Short EMA crosses above long EMA.
- Long/Short: Long only.
- Exit Criteria: Price hits stop loss or take profit.
- Stops: Fixed percentage stop loss and take profit.
- Default Values:
CandleType= TimeSpan.FromMinutes(1)ShortLength= 21LongLength= 50StopLossPercent= 1.0mTakeProfitPercent= 2.0m
- Filters:
- Category: Trend-following
- Direction: Long
- Indicators: EMA
- Stops: Yes
- Complexity: Beginner
- Timeframe: Any
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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>
/// EMA cloud crossover strategy that trades long when short EMA crosses above long EMA
/// and short when short EMA crosses below long EMA.
/// </summary>
public class LongOnlyMtfEmaCloudStrategy : Strategy
{
private readonly StrategyParam<int> _shortLength;
private readonly StrategyParam<int> _longLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<DataType> _candleType;
private EMA _shortEma;
private EMA _longEma;
private decimal _prevShort;
private decimal _prevLong;
private decimal _entryPrice;
private bool _isInitialized;
private int _cooldown;
public int ShortLength { get => _shortLength.Value; set => _shortLength.Value = value; }
public int LongLength { get => _longLength.Value; set => _longLength.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public LongOnlyMtfEmaCloudStrategy()
{
_shortLength = Param(nameof(ShortLength), 25)
.SetGreaterThanZero()
.SetDisplay("Short EMA", "Short EMA period", "Indicators");
_longLength = Param(nameof(LongLength), 65)
.SetGreaterThanZero()
.SetDisplay("Long EMA", "Long EMA period", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 7m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percent", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 12m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevShort = default;
_prevLong = default;
_entryPrice = default;
_isInitialized = false;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_shortEma = new EMA { Length = ShortLength };
_longEma = new EMA { Length = LongLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_shortEma, _longEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _shortEma);
DrawIndicator(area, _longEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal shortValue, decimal longValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_shortEma.IsFormed || !_longEma.IsFormed)
return;
if (!_isInitialized)
{
_prevShort = shortValue;
_prevLong = longValue;
_isInitialized = true;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevShort = shortValue;
_prevLong = longValue;
return;
}
var crossedUp = _prevShort <= _prevLong && shortValue > longValue;
var crossedDown = _prevShort >= _prevLong && shortValue < longValue;
// Close short and go long on bullish cross
if (crossedUp && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket();
_entryPrice = candle.ClosePrice;
_cooldown = 10;
}
// Close long and go short on bearish cross
else if (crossedDown && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket();
_entryPrice = candle.ClosePrice;
_cooldown = 10;
}
// Stop loss / take profit for long
if (Position > 0 && _entryPrice > 0)
{
var sl = _entryPrice * (1m - StopLossPercent / 100m);
var tp = _entryPrice * (1m + TakeProfitPercent / 100m);
if (candle.ClosePrice <= sl || candle.ClosePrice >= tp)
{
SellMarket(Math.Abs(Position));
_cooldown = 20;
}
}
// Stop loss / take profit for short
if (Position < 0 && _entryPrice > 0)
{
var sl = _entryPrice * (1m + StopLossPercent / 100m);
var tp = _entryPrice * (1m - TakeProfitPercent / 100m);
if (candle.ClosePrice >= sl || candle.ClosePrice <= tp)
{
BuyMarket(Math.Abs(Position));
_cooldown = 20;
}
}
_prevShort = shortValue;
_prevLong = longValue;
}
}
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 long_only_mtf_ema_cloud_strategy(Strategy):
def __init__(self):
super(long_only_mtf_ema_cloud_strategy, self).__init__()
self._short_length = self.Param("ShortLength", 25) \
.SetGreaterThanZero() \
.SetDisplay("Short EMA", "Short EMA period", "Indicators")
self._long_length = self.Param("LongLength", 65) \
.SetGreaterThanZero() \
.SetDisplay("Long EMA", "Long EMA period", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 7.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop loss percent", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 12.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit %", "Take profit percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10))) \
.SetDisplay("Candle Type", "Candles", "General")
self._prev_short = 0.0
self._prev_long = 0.0
self._entry_price = 0.0
self._is_initialized = False
self._cooldown = 0
@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(long_only_mtf_ema_cloud_strategy, self).OnReseted()
self._prev_short = 0.0
self._prev_long = 0.0
self._entry_price = 0.0
self._is_initialized = False
self._cooldown = 0
def OnStarted2(self, time):
super(long_only_mtf_ema_cloud_strategy, self).OnStarted2(time)
self._prev_short = 0.0
self._prev_long = 0.0
self._entry_price = 0.0
self._is_initialized = False
self._cooldown = 0
self._short_ema = ExponentialMovingAverage()
self._short_ema.Length = self._short_length.Value
self._long_ema = ExponentialMovingAverage()
self._long_ema.Length = self._long_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._short_ema, self._long_ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._short_ema)
self.DrawIndicator(area, self._long_ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, short_val, long_val):
if candle.State != CandleStates.Finished:
return
if not self._short_ema.IsFormed or not self._long_ema.IsFormed:
return
sv = float(short_val)
lv = float(long_val)
if not self._is_initialized:
self._prev_short = sv
self._prev_long = lv
self._is_initialized = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_short = sv
self._prev_long = lv
return
close = float(candle.ClosePrice)
crossed_up = self._prev_short <= self._prev_long and sv > lv
crossed_down = self._prev_short >= self._prev_long and sv < lv
if crossed_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(abs(self.Position))
self.BuyMarket()
self._entry_price = close
self._cooldown = 10
elif crossed_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket(abs(self.Position))
self.SellMarket()
self._entry_price = close
self._cooldown = 10
sl_pct = float(self._stop_loss_percent.Value) / 100.0
tp_pct = float(self._take_profit_percent.Value) / 100.0
if self.Position > 0 and self._entry_price > 0.0:
sl = self._entry_price * (1.0 - sl_pct)
tp = self._entry_price * (1.0 + tp_pct)
if close <= sl or close >= tp:
self.SellMarket(abs(self.Position))
self._cooldown = 20
if self.Position < 0 and self._entry_price > 0.0:
sl = self._entry_price * (1.0 + sl_pct)
tp = self._entry_price * (1.0 - tp_pct)
if close >= sl or close <= tp:
self.BuyMarket(abs(self.Position))
self._cooldown = 20
self._prev_short = sv
self._prev_long = lv
def CreateClone(self):
return long_only_mtf_ema_cloud_strategy()