MAVA Xonax Strategy
This strategy uses exponential moving averages of open and close prices to detect direction changes. Stop loss and take profit distances are derived from high and low EMAs, ensuring trades have predefined risk and reward levels.
Details
- Entry Criteria:
- Long: EMA of open crosses above EMA of close using the last two completed bars.
- Short: EMA of open crosses below EMA of close using the last two completed bars.
- Long/Short: Both
- Stops: Fixed stop loss and take profit based on EMA ranges.
- Default Values:
EmaPeriod= 6CandleType= TimeSpan.FromMinutes(240).TimeFrame()
- Filters:
- Category: Reversal
- Direction: Both
- Indicators: EMA
- Stops: Yes
- Complexity: Basic
- Timeframe: Long-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on EMA cross of open and close prices with stop and take levels.
/// </summary>
public class MavaXonaxStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private EMA _emaClose;
private EMA _emaOpen;
private EMA _emaHigh;
private EMA _emaLow;
private decimal _prevOpen1;
private decimal _prevOpen2;
private decimal _prevClose1;
private decimal _prevClose2;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _longStop;
private decimal _longTake;
private decimal _shortStop;
private decimal _shortTake;
private int _history;
private int _cooldownRemaining;
/// <summary>
/// EMA period for all calculations.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SignalCooldownBars
{
get => _signalCooldownBars.Value;
set => _signalCooldownBars.Value = value;
}
public MavaXonaxStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 6)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 1)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait after an entry or exit", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(240).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevOpen1 = _prevOpen2 = 0m;
_prevClose1 = _prevClose2 = 0m;
_prevHigh = _prevLow = 0m;
_longStop = _longTake = 0m;
_shortStop = _shortTake = 0m;
_history = 0;
_cooldownRemaining = 0;
_emaClose = _emaOpen = _emaHigh = _emaLow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_emaClose = new EMA { Length = EmaPeriod };
_emaOpen = new EMA { Length = EmaPeriod };
_emaHigh = new EMA { Length = EmaPeriod };
_emaLow = new EMA { Length = EmaPeriod };
_cooldownRemaining = 0;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_emaClose, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal closeEma)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var openValue = _emaOpen!.Process(candle.OpenPrice, candle.ServerTime, true);
var highValue = _emaHigh!.Process(candle.HighPrice, candle.ServerTime, true);
var lowValue = _emaLow!.Process(candle.LowPrice, candle.ServerTime, true);
if (!openValue.IsFinal || !highValue.IsFinal || !lowValue.IsFinal)
return;
var openEma = openValue.ToDecimal();
var highEma = highValue.ToDecimal();
var lowEma = lowValue.ToDecimal();
// Check for stop loss or take profit hits.
if (Position > 0)
{
if (candle.ClosePrice <= _longStop || candle.ClosePrice >= _longTake)
{
SellMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
else if (Position < 0)
{
if (candle.ClosePrice >= _shortStop || candle.ClosePrice <= _shortTake)
{
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
if (_history >= 2 && _cooldownRemaining == 0 && IsFormedAndOnlineAndAllowTrading())
{
var step = Security.PriceStep ?? 1m;
var buySignal = _prevOpen2 > _prevClose2 && _prevOpen1 < _prevClose1;
var sellSignal = _prevOpen2 < _prevClose2 && _prevOpen1 > _prevClose1;
if (buySignal && Position == 0)
{
var takePr = _prevHigh - _prevLow;
if (takePr < 600m * step)
takePr = 600m * step;
var stopL = 2m * (_prevOpen1 - _prevLow);
if (stopL > 400m * step)
stopL = 400m * step;
var entry = candle.ClosePrice;
_longStop = entry - stopL;
_longTake = entry + takePr;
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
else if (sellSignal && Position == 0)
{
var takePr = _prevHigh - _prevLow;
if (takePr < 600m * step)
takePr = 600m * step;
var stopL = 2m * (_prevHigh - _prevClose1);
if (stopL > 400m * step)
stopL = 400m * step;
var entry = candle.ClosePrice;
_shortStop = entry + stopL;
_shortTake = entry - takePr;
SellMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
// Shift stored EMA values.
_prevOpen2 = _prevOpen1;
_prevOpen1 = openEma;
_prevClose2 = _prevClose1;
_prevClose1 = closeEma;
_prevHigh = highEma;
_prevLow = lowEma;
if (_history < 2)
_history++;
}
}
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
from indicator_extensions import *
class mava_xonax_strategy(Strategy):
"""
Mava Xonax: EMA cross of open/close prices with SL/TP.
"""
def __init__(self):
super(mava_xonax_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 6).SetDisplay("EMA Period", "EMA period", "General")
self._cooldown_bars = self.Param("SignalCooldownBars", 1).SetDisplay("Signal Cooldown", "Bars to wait after an entry or exit", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(240))).SetDisplay("Candle Type", "Candles", "General")
self._prev_open1 = 0.0
self._prev_open2 = 0.0
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._long_stop = 0.0
self._long_take = 0.0
self._short_stop = 0.0
self._short_take = 0.0
self._history = 0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mava_xonax_strategy, self).OnReseted()
self._prev_open1 = 0.0
self._prev_open2 = 0.0
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._long_stop = 0.0
self._long_take = 0.0
self._short_stop = 0.0
self._short_take = 0.0
self._history = 0
self._cooldown_remaining = 0
self._ema_close = None
self._ema_open = None
self._ema_high = None
self._ema_low = None
def OnStarted2(self, time):
super(mava_xonax_strategy, self).OnStarted2(time)
self._ema_close = ExponentialMovingAverage()
self._ema_close.Length = self._ema_period.Value
self._ema_open = ExponentialMovingAverage()
self._ema_open.Length = self._ema_period.Value
self._ema_high = ExponentialMovingAverage()
self._ema_high.Length = self._ema_period.Value
self._ema_low = ExponentialMovingAverage()
self._ema_low.Length = self._ema_period.Value
self._cooldown_remaining = 0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema_close, self._process_candle).Start()
def _process_candle(self, candle, close_ema_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
close_ema = float(close_ema_val)
open_result = process_float(self._ema_open, candle.OpenPrice, candle.ServerTime, True)
high_result = process_float(self._ema_high, candle.HighPrice, candle.ServerTime, True)
low_result = process_float(self._ema_low, candle.LowPrice, candle.ServerTime, True)
if not open_result.IsFinal or not high_result.IsFinal or not low_result.IsFinal:
return
open_ema = float(open_result)
high_ema = float(high_result)
low_ema = float(low_result)
close = float(candle.ClosePrice)
if self.Position > 0:
if close <= self._long_stop or close >= self._long_take:
self.SellMarket()
self._cooldown_remaining = self._cooldown_bars.Value
elif self.Position < 0:
if close >= self._short_stop or close <= self._short_take:
self.BuyMarket()
self._cooldown_remaining = self._cooldown_bars.Value
if self._history >= 2 and self._cooldown_remaining == 0 and self.IsFormedAndOnlineAndAllowTrading():
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
buy_signal = self._prev_open2 > self._prev_close2 and self._prev_open1 < self._prev_close1
sell_signal = self._prev_open2 < self._prev_close2 and self._prev_open1 > self._prev_close1
if buy_signal and self.Position == 0:
take_pr = self._prev_high - self._prev_low
if take_pr < 600.0 * step:
take_pr = 600.0 * step
stop_l = 2.0 * (self._prev_open1 - self._prev_low)
if stop_l > 400.0 * step:
stop_l = 400.0 * step
self._long_stop = close - stop_l
self._long_take = close + take_pr
self.BuyMarket()
self._cooldown_remaining = self._cooldown_bars.Value
elif sell_signal and self.Position == 0:
take_pr = self._prev_high - self._prev_low
if take_pr < 600.0 * step:
take_pr = 600.0 * step
stop_l = 2.0 * (self._prev_high - self._prev_close1)
if stop_l > 400.0 * step:
stop_l = 400.0 * step
self._short_stop = close + stop_l
self._short_take = close - take_pr
self.SellMarket()
self._cooldown_remaining = self._cooldown_bars.Value
self._prev_open2 = self._prev_open1
self._prev_open1 = open_ema
self._prev_close2 = self._prev_close1
self._prev_close1 = close_ema
self._prev_high = high_ema
self._prev_low = low_ema
if self._history < 2:
self._history += 1
def CreateClone(self):
return mava_xonax_strategy()