XIT Three MA Cross Strategy
This strategy is a StockSharp recreation of the MetaTrader 5 expert advisor XIT_THREE_MA_CROSS.mq5. It aligns three moving averages, checks MACD momentum separation, and sizes positions from ATR-based risk limits. The method is trend-following with momentum confirmation and targets medium-term swings on liquid currency pairs or indices.
Overview
- Market Regime: Works best in instruments that trend for multiple candles on the selected timeframe.
- Indicators:
- Slow, intermediate, and fast moving averages (user-selectable type) evaluated on the trading timeframe.
- MACD (EMA-based) for momentum direction and distance between MACD and signal line.
- Two ATR calculations (same length, independent timeframes) used to project stop-loss and take-profit distances.
- Order Direction: Bi-directional. The engine can open both long and short trades.
- Position Sizing: Calculated from the configured risk percentage and the ATR-based stop distance. When instrument metadata is incomplete, the strategy falls back to the default
Volume property.
Trading Logic
Long Entry
A long position is opened when all conditions below are true on a finished candle:
- MACD line increases compared to the previous bar (
MACD[t] > MACD[t-1]).
- MACD signal line increases compared to the previous bar.
- The MACD line exceeds the signal line by at least
MacdTriggerPoints * PriceStep.
- Intermediate moving average rises vs the previous value.
- Fast moving average rises vs the previous value.
- Intermediate MA is above the slow MA.
- Fast MA is above the intermediate MA.
- Both ATR values are available to define stop and target distances.
Short Entry
The short-side rules mirror the long setup with inverted comparisons:
- MACD line decreases compared to the previous bar.
- MACD signal line decreases compared to the previous bar.
- The signal line is greater than the MACD line by at least
MacdTriggerPoints * PriceStep.
- Intermediate MA falls compared to the prior candle.
- Fast MA falls compared to the prior candle.
- Intermediate MA is below the slow MA.
- Fast MA is below the intermediate MA.
- Both ATR series have delivered a finished value.
Exit Logic
- Long positions close when the fast MA drops below the intermediate MA, or price hits the ATR-based stop/take-profit levels.
- Short positions close when the fast MA crosses above the intermediate MA, or the ATR limits are touched.
- After closing a position, the algorithm waits for the next candle before evaluating new entries, matching the original EA behavior.
Risk Management
- Stop Loss: Distance equals the latest ATR value from
AtrStopCandleType. For longs the stop price is Entry - ATR, for shorts it is Entry + ATR.
- Take Profit: Distance equals the ATR value from
AtrTakeCandleType. Targets are mirrored relative to entry price.
- Risk Percent: The strategy estimates the monetary loss per unit from the stop distance. If
PriceStep and PriceStepCost are known, the risk per contract uses tick valuation. Otherwise, the raw price distance is used. Position size is RiskPercent% of the current portfolio value divided by the per-unit risk, rounded down to the nearest VolumeStep.
Parameters
| Name |
Description |
Default |
CandleType |
Primary timeframe for moving averages and MACD calculations. |
1-hour candles |
SlowMaLength / IntermediateMaLength / FastMaLength |
Periods of the moving averages. |
60 / 14 / 4 |
SlowMaType, IntermediateMaType, FastMaType |
Moving average families (Simple, Exponential, Smoothed, Weighted). |
Simple |
MacdFastLength, MacdSlowLength, MacdSignalLength |
MACD fast, slow, and signal EMA lengths. |
12 / 26 / 9 |
MacdTriggerPoints |
Minimum distance between MACD and its signal, measured in instrument points. Converted using PriceStep. |
7 |
AtrLength |
Period for both ATR indicators. |
14 |
AtrTakeCandleType / AtrStopCandleType |
Timeframes for take-profit and stop-loss ATR series. |
4-hour candles |
RiskPercent |
Percent of current portfolio value risked on each trade. |
10% |
Usage Notes
- Attach the strategy to a security with accurate
PriceStep, PriceStepCost, and VolumeStep to obtain precise position sizing.
- Ensure historical data is available for every subscribed timeframe (
CandleType, AtrTakeCandleType, AtrStopCandleType). Missing ATR values will postpone entries.
- The algorithm operates on fully closed candles and ignores intrabar fluctuations, mirroring the original MetaTrader logic of fetching current and previous indicator buffers.
- Modify the moving-average types if the target market favors smoother or faster filters.
Files
CS/XitThreeMaCrossStrategy.cs – C# implementation with high-level StockSharp API, including ATR subscriptions and risk sizing.
README_ru.md – Russian description of the strategy.
README_zh.md – Chinese translation of the documentation.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Triple MA alignment strategy.
/// </summary>
public class XitThreeMaCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _midPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevMid;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int MidPeriod
{
get => _midPeriod.Value;
set => _midPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public XitThreeMaCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast MA period", "Indicators");
_midPeriod = Param(nameof(MidPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Mid Period", "Medium MA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow MA (trend filter)", "Indicators");
Volume = 0.1m;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevMid = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevMid = null;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var mid = new SimpleMovingAverage { Length = MidPeriod };
var slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, mid, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, mid);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal midVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == null || _prevMid == null)
{
_prevFast = fastVal;
_prevMid = midVal;
return;
}
var buySignal = fastVal > midVal && midVal > slowVal;
var sellSignal = fastVal < midVal && midVal < slowVal;
if (buySignal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
}
else if (sellSignal && Position >= 0)
{
if (Position > 0)
SellMarket(Position);
SellMarket(Volume);
}
_prevFast = fastVal;
_prevMid = midVal;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class xit_three_ma_cross_strategy(Strategy):
def __init__(self):
super(xit_three_ma_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast Period", "Fast MA period", "Indicators")
self._mid_period = self.Param("MidPeriod", 20) \
.SetDisplay("Mid Period", "Medium MA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA (trend filter)", "Indicators")
self._prev_fast = None
self._prev_mid = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def MidPeriod(self):
return self._mid_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(xit_three_ma_cross_strategy, self).OnReseted()
self._prev_fast = None
self._prev_mid = None
def OnStarted2(self, time):
super(xit_three_ma_cross_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_mid = None
fast = SimpleMovingAverage()
fast.Length = self.FastPeriod
mid = SimpleMovingAverage()
mid.Length = self.MidPeriod
slow = SimpleMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, mid, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, mid)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, mid_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
mv = float(mid_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_mid is None:
self._prev_fast = fv
self._prev_mid = mv
return
buy_signal = fv > mv and mv > sv
sell_signal = fv < mv and mv < sv
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fv
self._prev_mid = mv
def CreateClone(self):
return xit_three_ma_cross_strategy()