This strategy replicates the logic of the original X_alert_3.mq4 expert. It monitors two moving averages with configurable parameters and produces an informational alert when a crossover occurs.
How it works
Two moving averages are calculated on every finished candle.
A bullish alert is generated when:
MA1 is above MA2 on the current candle.
MA1 is above MA2 on the previous candle.
MA1 was below MA2 two candles ago.
A bearish alert is generated when:
MA1 is below MA2 on the current candle.
MA1 is below MA2 on the previous candle.
MA1 was above MA2 two candles ago.
The strategy does not open or close any positions. It only writes messages to the log.
Parameters
Parameter
Description
Default
Ma1Period
Period of the first moving average.
1
Ma1Type
Type of the first moving average (Simple, Exponential, Smoothed, Weighted).
Simple
Ma2Period
Period of the second moving average.
14
Ma2Type
Type of the second moving average.
Simple
PriceType
Source price used in calculations (Close, Open, High, Low, Median, Typical, Weighted).
Median
CandleType
Candle series used for processing.
1-minute time frame
Notes
The implementation keeps track of the last two differences between the moving averages to detect crossovers without accessing historical indicator values directly.
Alerts are written using AddInfoLog to keep the strategy side-effect free.
The original MetaTrader parameter RunIntervalSeconds is not required in StockSharp and has been omitted.
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>
/// Strategy based on crossover of two moving averages.
/// Buys when fast MA crosses above slow MA, sells when crosses below.
/// </summary>
public class XAlert3Strategy : Strategy
{
private readonly StrategyParam<int> _ma1Period;
private readonly StrategyParam<int> _ma2Period;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int Ma1Period { get => _ma1Period.Value; set => _ma1Period.Value = value; }
public int Ma2Period { get => _ma2Period.Value; set => _ma2Period.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public XAlert3Strategy()
{
_ma1Period = Param(nameof(Ma1Period), 10)
.SetGreaterThanZero()
.SetDisplay("MA1 Period", "Fast moving average period", "Indicators");
_ma2Period = Param(nameof(Ma2Period), 30)
.SetGreaterThanZero()
.SetDisplay("MA2 Period", "Slow moving average period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = Ma1Period };
var slow = new ExponentialMovingAverage { Length = Ma2Period };
SubscribeCandles(CandleType)
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
if (_prevFast <= _prevSlow && fast > slow)
{
if (Position < 0) BuyMarket();
if (Position <= 0) BuyMarket();
}
else if (_prevFast >= _prevSlow && fast < slow)
{
if (Position > 0) SellMarket();
if (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 x_alert3_strategy(Strategy):
def __init__(self):
super(x_alert3_strategy, self).__init__()
self._ma1_period = self.Param("Ma1Period", 10) \
.SetDisplay("MA1 Period", "Fast moving average period", "Indicators")
self._ma2_period = self.Param("Ma2Period", 30) \
.SetDisplay("MA2 Period", "Slow moving average period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def ma1_period(self):
return self._ma1_period.Value
@property
def ma2_period(self):
return self._ma2_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(x_alert3_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(x_alert3_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self.ma1_period
slow = ExponentialMovingAverage()
slow.Length = self.ma2_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fast > slow:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fast < slow:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return x_alert3_strategy()