The Forex Sky Strategy is a direct port of the MetaTrader expert advisor Forex_SKY.mq4. It trades MACD momentum swings and strictly limits itself to a single position per trading day. The StockSharp implementation keeps the original MACD thresholds and the safety check that prevents more than one order per candle.
The strategy subscribes to the timeframe defined by CandleType (15-minute candles by default) and evaluates the classic MACD (12/26/9) on the close of each completed candle.
Trading Logic
Long entry – Place a market buy when:
The current MACD main line is above zero;
It also exceeds +0.00009 to confirm momentum;
At least one of the previous three MACD readings was less than or equal to zero (capturing a bullish flip from negative territory).
Short entry – Place a market sell when either of the following is true:
The MACD main line is below zero, drops below -0.0004, at least one of the last three readings was non-negative, and the value from four bars ago was at least +0.001.
Or the value from four bars ago was ≥ +0.003, which immediately authorises a short trade just like in the original MetaTrader code.
Position management – The algorithm never opens more than one order per candle (Time0 guard) and never trades more than once per calendar day (CheckTodaysOrders guard). Protective exit orders are handled by the StockSharp StartProtection helper, so all stops and targets remain synchronized with the current volume.
There is no autonomous flatting logic beyond the protective orders—positions are expected to be closed by take-profit, stop-loss or manual intervention, mirroring the behaviour of the original expert advisor.
Parameters
Name
Default
Description
FastPeriod
12
Fast EMA length of the MACD indicator.
SlowPeriod
26
Slow EMA length of the MACD indicator.
SignalPeriod
9
Signal EMA length of the MACD indicator.
TakeProfitPoints
100
Distance to the take-profit order expressed in instrument points. Converted to price by multiplying with the security price step.
StopLossPoints
3000
Distance to the stop-loss order in instrument points.
TradeVolume
0.1
Base market order size (lots).
CandleType
15-minute time frame
Timeframe that feeds the MACD calculations and trade decisions.
Instrument point calculation
TakeProfitPoints and StopLossPoints are specified exactly like the MetaTrader version—Point in MQL4 corresponds to Security.PriceStep in StockSharp. For a five-digit forex pair (PriceStep = 0.00001), the default settings translate to:
Take-profit: 100 × 0.00001 = 0.001 price units.
Stop-loss: 3000 × 0.00001 = 0.03 price units.
Risk Management
StartProtection automatically installs the take-profit and stop-loss orders after an entry is filled. They are linked to the trade direction and use market orders when triggered, matching the MetaTrader behaviour. Set either parameter to 0 to disable the corresponding protective order.
Migration Notes
The MACD history buffer keeps the last four completed values in class fields so no indicator calls with shifted indices are required.
Daily trade throttling and the single-trade-per-bar constraint replicate CheckTodaysOrders() and Time0 from the original source.
All comments were rewritten in English, and the logic relies on StockSharp high-level bindings (Bind) for indicator processing.
Usage Tips
Adjust CandleType to the chart period you want to emulate; the original script inherits the chart timeframe automatically.
Because only one trade is allowed per day, pick markets with meaningful intraday swings or consider raising the MACD thresholds when using higher volatility instruments.
Monitor the platform clock/time zone to ensure the day boundary matches your trading session, as the limit counter resets based on the candle open date.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Forex Sky: MACD zero-line cross with EMA trend filter.
/// </summary>
public class ForexSkyStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevMacd;
private decimal _entryPrice;
public ForexSkyStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastLength = Param(nameof(FastLength), 12)
.SetDisplay("Fast EMA", "MACD fast period.", "Indicators");
_slowLength = Param(nameof(SlowLength), 26)
.SetDisplay("Slow EMA", "MACD slow period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = 0;
_entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var macd = fastVal - slowVal;
var close = candle.ClosePrice;
if (_prevMacd == 0)
{
_prevMacd = macd;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (macd < 0 && _prevMacd >= 0))
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (macd > 0 && _prevMacd <= 0))
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (macd > 0 && _prevMacd <= 0 && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (macd < 0 && _prevMacd >= 0 && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevMacd = macd;
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class forex_sky_strategy(Strategy):
"""
Forex Sky: MACD zero-line cross with EMA trend filter and ATR-based exits.
"""
def __init__(self):
super(forex_sky_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast EMA", "MACD fast period", "Indicators")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow EMA", "MACD slow period", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._prev_macd = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(forex_sky_strategy, self).OnReseted()
self._prev_macd = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(forex_sky_strategy, self).OnStarted2(time)
self._prev_macd = 0.0
self._entry_price = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, ema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_val)
slow_val = float(slow_val)
ema_val = float(ema_val)
atr_val = float(atr_val)
if atr_val <= 0:
return
macd = fast_val - slow_val
close = float(candle.ClosePrice)
if self._prev_macd == 0:
self._prev_macd = macd
return
if self.Position > 0:
if (close >= self._entry_price + atr_val * 3.0 or
close <= self._entry_price - atr_val * 2.0 or
(macd < 0 and self._prev_macd >= 0)):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (close <= self._entry_price - atr_val * 3.0 or
close >= self._entry_price + atr_val * 2.0 or
(macd > 0 and self._prev_macd <= 0)):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if macd > 0 and self._prev_macd <= 0 and close > ema_val:
self._entry_price = close
self.BuyMarket()
elif macd < 0 and self._prev_macd >= 0 and close < ema_val:
self._entry_price = close
self.SellMarket()
self._prev_macd = macd
def CreateClone(self):
return forex_sky_strategy()