The Sample Detect Economic Calendar Strategy replicates the behaviour of the original MetaTrader expert advisor SampleDetectEconomicCalendar.mq5. The strategy watches a manually provided list of economic calendar events, and—when a high impact event is approaching for the configured currency—places a symmetric pair of stop orders around the current bid/ask prices. Protective stops, optional take profit levels, and a trailing exit replicate the money-management logic from the source code.
Unlike the MQL version, the StockSharp port does not have access to the MetaTrader calendar service. Instead, events are supplied by the user through the CalendarDefinition parameter.
How it works
The strategy subscribes to Level1 data to track bid/ask prices.
Calendar lines defined in CalendarDefinition are parsed on start-up.
For each high importance event matching the BaseCurrency, the strategy:
Waits until LeadMinutes before the release.
Calculates the order volume (either fixed or risk-based).
Places buy/sell stop orders at BuyDistancePoints and SellDistancePoints from the current prices.
After the release, pending orders are cancelled once PostMinutes elapse or after the total ExpiryMinutes timeout.
When one side is triggered the opposite order is cancelled. The open position is managed with stop loss, optional take profit, and trailing stop distances expressed in points.
Parameters
Parameter
Description
TradeNews
Enables placing pending orders around scheduled news events.
OrderVolume
Fixed order volume used when money management is disabled.
StopLossPoints
Stop-loss distance in instrument points. Set to 0 to disable.
TakeProfitPoints
Take-profit distance in points. Set to 0 to disable.
TrailingStopPoints
Trailing stop distance in points. Set to 0 to disable trailing.
ExpiryMinutes
Maximum lifetime of pending orders after the release.
UseMoneyManagement
If enabled, the volume is calculated from balance risk.
RiskPercent
Percentage of portfolio capital risked per trade (used only when money management is active).
BuyDistancePoints
Offset above the ask for the buy stop entry.
SellDistancePoints
Offset below the bid for the sell stop entry.
LeadMinutes
Minutes before release when pending orders are submitted.
PostMinutes
Minutes after release before unattended orders are cancelled.
BaseCurrency
Currency code that must appear in the calendar entry (default USD).
CalendarDefinition
Multiline string containing calendar events.
Calendar definition format
Provide one event per line in the following format:
yyyy-MM-dd HH:mm;CUR;High;Event title
yyyy-MM-dd HH:mm — timestamp in UTC. Seconds are optional. Multiple date formats (yyyy/MM/dd, dd.MM.yyyy) are also supported.
CUR — currency code (e.g. USD). Only events matching BaseCurrency are traded.
High — importance keyword (High, Medium, Low, or Nfp). Only High triggers trades.
When UseMoneyManagement is off, orders are placed using the OrderVolume parameter.
When UseMoneyManagement is on, the strategy risks RiskPercent of the portfolio value using the configured StopLossPoints. Exchange volume limits (min/max step) are respected.
Trailing logic mirrors the original EA: the stop-loss and take-profit exits are enforced, and once price moves favourably by TrailingStopPoints, the trailing stop protects the trade.
Differences from the MQL expert advisor
Economic calendar events must be provided manually in CalendarDefinition.
Only one instrument/currency pair is processed per strategy instance.
Pending order expiration is handled internally with PostMinutes/ExpiryMinutes timers because StockSharp stop orders do not expose MetaTrader-style ORDER_TIME_SPECIFIED flags.
Usage notes
Configure the CalendarDefinition lines before starting the strategy.
Enable TradeNews and set the desired risk parameters.
Ensure Level1 data is available so that bid/ask updates arrive before the news window.
Review the logs to confirm orders are placed and cancelled as expected around each event.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Sample Detect Economic Calendar strategy: Parabolic SAR trend with EMA filter.
/// Buys when close above both SAR and EMA, sells when close below both.
/// </summary>
public class SampleDetectEconomicCalendarStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal _prevClose;
private decimal _prevSar;
private decimal _prevEma;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public SampleDetectEconomicCalendarStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA filter period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevSar = 0;
_prevEma = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevSar = 0;
_prevEma = 0;
_hasPrev = false;
var sar = new ParabolicSar { Acceleration = 0.01m, AccelerationMax = 0.1m };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sar, ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sarValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
var aboveSar = candle.ClosePrice > sarValue;
var belowSar = candle.ClosePrice < sarValue;
var aboveEma = candle.ClosePrice > emaValue;
var belowEma = candle.ClosePrice < emaValue;
if (aboveSar && aboveEma && !(_prevClose > _prevSar && _prevClose > _prevEma) && Position <= 0)
BuyMarket();
else if (belowSar && belowEma && !(_prevClose < _prevSar && _prevClose < _prevEma) && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevSar = sarValue;
_prevEma = emaValue;
_hasPrev = true;
}
}
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 ParabolicSar, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class sample_detect_economic_calendar_strategy(Strategy):
def __init__(self):
super(sample_detect_economic_calendar_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ema_period = self.Param("EmaPeriod", 50)
self._prev_close = 0.0
self._prev_sar = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
def OnReseted(self):
super(sample_detect_economic_calendar_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sar = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(sample_detect_economic_calendar_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_sar = 0.0
self._prev_ema = 0.0
self._has_prev = False
sar = ParabolicSar()
sar.Acceleration = 0.01
sar.AccelerationMax = 0.1
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sar, ema, self._process_candle).Start()
def _process_candle(self, candle, sar_value, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sar_val = float(sar_value)
ema_val = float(ema_value)
if self._has_prev:
above_sar = close > sar_val
below_sar = close < sar_val
above_ema = close > ema_val
below_ema = close < ema_val
was_above_both = self._prev_close > self._prev_sar and self._prev_close > self._prev_ema
was_below_both = self._prev_close < self._prev_sar and self._prev_close < self._prev_ema
if above_sar and above_ema and not was_above_both and self.Position <= 0:
self.BuyMarket()
elif below_sar and below_ema and not was_below_both and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_sar = sar_val
self._prev_ema = ema_val
self._has_prev = True
def CreateClone(self):
return sample_detect_economic_calendar_strategy()