This strategy trades based on the direction of a third-generation moving average. The indicator is a combination of two exponential moving averages and turns blue when rising and pink when falling. A buy signal is recorded when the average turns upward, and a sell signal is recorded when it turns downward.
Orders are placed only at a user-specified time after a signal appears. Positions can also be closed when the opposite signal is detected or when a predefined holding period expires. Optional stop-loss and take-profit levels are measured in points.
Parameters
Length – smoothing period of the third-generation average.
StartHour – hour when new positions may be opened.
StartMinute – minute within the hour when openings are allowed.
HoldMinutes – maximum time to keep an open position.
Volume – order volume used for entries.
StopLoss – stop-loss distance in points. 0 disables the stop.
TakeProfit – take-profit distance in points. 0 disables the target.
UseLongEntries – enable long entries.
UseShortEntries – enable short entries.
CloseLongBySignal – close long positions when a sell signal appears.
CloseShortBySignal – close short positions when a buy signal appears.
CandleType – timeframe of candles used for calculations.
Logic
Subscribe to candles of the selected timeframe.
Compute the third-generation moving average for each candle.
Detect when the average rises or falls between consecutive candles.
Store a buy or sell signal based on the direction change.
At the specified opening time, enter in the direction of the stored signal.
Close positions on opposite signals, when the holding time elapses, or when stop-loss/take-profit levels are reached.
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>
/// Color 3rd Generation XMA strategy.
/// Uses two EMAs of different periods to approximate 3rd generation moving average.
/// Opens long when fast EMA crosses above slow EMA, short when crosses below.
/// </summary>
public class Color3RdGenXmaStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _minSpread;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _isFirst = true;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public decimal MinSpread { get => _minSpread.Value; set => _minSpread.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Color3RdGenXmaStrategy()
{
_fastLength = Param(nameof(FastLength), 20)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "General");
_slowLength = Param(nameof(SlowLength), 50)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "General");
_minSpread = Param(nameof(MinSpread), 20m)
.SetGreaterThanZero()
.SetDisplay("Min Spread", "Minimum EMA spread in price steps", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_isFirst = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0m;
_prevSlow = 0m;
_isFirst = true;
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (_isFirst)
{
_prevFast = fast;
_prevSlow = slow;
_isFirst = false;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var step = Security.PriceStep ?? 1m;
var spread = Math.Abs(fast - slow);
if (spread < MinSpread * step)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var prevAbove = _prevFast > _prevSlow;
var curAbove = fast > slow;
if (!prevAbove && curAbove && Position <= 0)
BuyMarket();
else if (prevAbove && !curAbove && Position >= 0)
SellMarket();
_prevFast = fast;
_prevSlow = slow;
}
}
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 color_3rd_gen_xma_strategy(Strategy):
def __init__(self):
super(color_3rd_gen_xma_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 20) \
.SetDisplay("Fast EMA", "Fast EMA period", "General")
self._slow_length = self.Param("SlowLength", 50) \
.SetDisplay("Slow EMA", "Slow EMA period", "General")
self._min_spread = self.Param("MinSpread", 20.0) \
.SetDisplay("Min Spread", "Minimum EMA spread in price steps", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_first = True
@property
def fast_length(self):
return self._fast_length.Value
@property
def slow_length(self):
return self._slow_length.Value
@property
def min_spread(self):
return self._min_spread.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_3rd_gen_xma_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_first = True
def OnStarted2(self, time):
super(color_3rd_gen_xma_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_first = True
fast_ema = ExponentialMovingAverage()
fast_ema.Length = int(self.fast_length)
slow_ema = ExponentialMovingAverage()
slow_ema.Length = int(self.slow_length)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast = float(fast)
slow = float(slow)
if self._is_first:
self._prev_fast = fast
self._prev_slow = slow
self._is_first = False
return
spread = abs(fast - slow)
ms = float(self.min_spread)
price_step = float(self.Security.PriceStep) if self.Security.PriceStep is not None else 1.0
if spread < ms * price_step:
self._prev_fast = fast
self._prev_slow = slow
return
prev_above = self._prev_fast > self._prev_slow
cur_above = fast > slow
if not prev_above and cur_above and self.Position <= 0:
self.BuyMarket()
elif prev_above and not cur_above and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return color_3rd_gen_xma_strategy()