Стратегия Trade EA Template for News
Общее описание
Trade EA Template for News Strategy — это порт на C# экспертного советника MetaTrader 4 «Trade EA Template for News». Оригинальная версия отключала торговлю перед и после выхода макроэкономических новостей, которые подгружались с внешних сайтов. Реализация для StockSharp сохраняет идею фильтра новостей и переносит её на высокоуровневый API:
- Работает с завершёнными свечами выбранного таймфрейма (по умолчанию H1).
- Открывает позиции только при отсутствии активных сделок, что полностью повторяет условие
OrdersTotal()<1 из MQL.
- Блокирует открытия сделок в заданные интервалы до и после новостей в зависимости от их важности.
- Автоматически ставит защитные стоп-лосс и тейк-профит на расстоянии 100 пунктов, рассчитанных через шаг цены инструмента.
Логика торговли
- На каждой закрытой свече стратегия пересчитывает расписание новостей. Открытие предыдущей свечи сохраняется, чтобы следующая свеча могла сравнить свой закрывающийся уровень с прошлым открытием.
- Если текущее время попадает в любой запретный интервал, все заявки отменяются, а новые сделки не открываются.
- При отсутствии позиции и разрешённой торговле:
- Покупка открывается, если закрытие последней свечи выше открытия предыдущей свечи.
- Продажа открывается, если закрытие последней свечи ниже открытия предыдущей свечи.
- Параметры
TakeProfitPoints и StopLossPoints задают расстояние до защитных ордеров в пунктах; фактический ценовой сдвиг рассчитывается умножением на Security.Step.
Ручное расписание новостей
Вместо автоматического скачивания календаря необходимо вручную заполнить параметр NewsEventsDefinition. В нём перечисляются события, разделённые точкой с запятой или переводом строки. Каждая запись должна содержать минимум три поля, разделённые запятыми:
ГГГГ-ММ-ДД ЧЧ:ММ,ВАЛЮТЫ,ВАЖНОСТЬ[,НАЗВАНИЕ]
ГГГГ-ММ-ДД ЧЧ:ММ — время события в UTC. Параметр TimeZoneOffsetHours сдвигает все события на указанное количество часов (например, 3 для UTC+3).
ВАЛЮТЫ — коды валют или обозначения инструментов (USD, EUR, EUR/USD). Несколько кодов можно разделять через /, ,, ;, | или пробелы.
ВАЖНОСТЬ — ключевое слово: поддерживаются Low, Medium, Mid, Midle, Moderate, High, NFP, а также любые строки, содержащие Nonfarm или Non-farm.
НАЗВАНИЕ — необязательное описание, которое будет отображаться в логах.
Пример:
2024-03-01 13:30,USD,High,Nonfarm Payrolls;2024-03-01 15:00,USD,Low,Factory Orders
Настройка запретных интервалов
- Флаги
UseLowNews, UseMediumNews, UseHighNews, UseNfpNews включают или выключают соответствующие группы событий.
- Пары параметров
LowMinutesBefore/After, MediumMinutesBefore/After, HighMinutesBefore/After, NfpMinutesBefore/After задают длительность запрета до и после новости.
- Параметр
OnlySymbolNews ограничивает фильтр событиями, содержащими валюты из тикера текущего инструмента (например, EURUSD → {EUR, USD}). При отключении блокируется торговля по всем событиям.
- Если одновременно действует несколько новостей, приоритет имеет событие с наибольшей важностью. В лог выводятся сообщения с указанием причины блокировки и ближайшего релиза.
Параметры
| Параметр |
Описание |
Значение по умолчанию |
CandleType |
Тип свечей для подписки (по умолчанию часовые). |
1h |
UseLowNews |
Учитывать новости низкой важности. |
true |
LowMinutesBefore / LowMinutesAfter |
Минуты до/после низковажных новостей. |
15 / 15 |
UseMediumNews |
Учитывать новости средней важности. |
true |
MediumMinutesBefore / MediumMinutesAfter |
Минуты до/после новостей средней важности. |
30 / 30 |
UseHighNews |
Учитывать новости высокой важности. |
true |
HighMinutesBefore / HighMinutesAfter |
Минуты до/после новостей высокой важности. |
60 / 60 |
UseNfpNews |
Учитывать события Non-farm Payrolls. |
true |
NfpMinutesBefore / NfpMinutesAfter |
Минуты до/после NFP. |
180 / 180 |
OnlySymbolNews |
Фильтровать события по валютам текущего инструмента. |
true |
NewsEventsDefinition |
Строка с расписанием новостей. |
пусто |
TimeZoneOffsetHours |
Сдвиг часов относительно UTC для всех событий. |
0 |
TakeProfitPoints |
Тейк-профит в пунктах. |
100 |
StopLossPoints |
Стоп-лосс в пунктах. |
100 |
Значение Volume наследуется от базового класса Strategy и определяет объём позиции.
Отличия от MQL-версии
- Нет автоматического веб-запроса — расписание формируется вручную, что делает стратегию автономной и воспроизводимой.
- Визуальные линии и подписи заменены логами вида «Trading paused due to…» и «Next scheduled news…».
- В MQL фиксированный объём 0.01 лота, в StockSharp объём задаётся через
Volume.
- Вся логика построена на высокоуровневой подписке свечей без прямых обращений к индикаторам.
Рекомендации по запуску
- Заполните
NewsEventsDefinition перед стартом (после изменения параметра перезапустите стратегию, чтобы перечитать список).
- Настройте
TimeZoneOffsetHours и длительность окон в соответствии с вашим торговым расписанием.
- Установите
Volume, выберите портфель и инструмент, затем запустите стратегию.
- Контролируйте журнал стратегии — там появятся сообщения о причинах паузы и времени ближайшей новости.
Python-версия намеренно не создаётся по требованию.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using System.Globalization;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trade EA Template for News strategy converted from MQL.
/// </summary>
public class TradeEaTemplateForNewsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _useLowNews;
private readonly StrategyParam<int> _lowMinutesBefore;
private readonly StrategyParam<int> _lowMinutesAfter;
private readonly StrategyParam<bool> _useMediumNews;
private readonly StrategyParam<int> _mediumMinutesBefore;
private readonly StrategyParam<int> _mediumMinutesAfter;
private readonly StrategyParam<bool> _useHighNews;
private readonly StrategyParam<int> _highMinutesBefore;
private readonly StrategyParam<int> _highMinutesAfter;
private readonly StrategyParam<bool> _useNfpNews;
private readonly StrategyParam<int> _nfpMinutesBefore;
private readonly StrategyParam<int> _nfpMinutesAfter;
private readonly StrategyParam<bool> _onlySymbolNews;
private readonly StrategyParam<string> _newsEventsDefinition;
private readonly StrategyParam<int> _timeZoneOffsetHours;
private readonly StrategyParam<int> _takeProfitPoints;
private readonly StrategyParam<int> _stopLossPoints;
private readonly List<NewsEvent> _newsEvents = new();
private readonly HashSet<string> _instrumentCurrencies = new(StringComparer.OrdinalIgnoreCase);
private decimal? _previousOpenPrice;
private bool _newsBlocking;
private string _lastNewsMessage = string.Empty;
public TradeEaTemplateForNewsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame());
_useLowNews = Param(nameof(UseLowNews), true);
_lowMinutesBefore = Param(nameof(LowMinutesBefore), 15);
_lowMinutesAfter = Param(nameof(LowMinutesAfter), 15);
_useMediumNews = Param(nameof(UseMediumNews), true);
_mediumMinutesBefore = Param(nameof(MediumMinutesBefore), 30);
_mediumMinutesAfter = Param(nameof(MediumMinutesAfter), 30);
_useHighNews = Param(nameof(UseHighNews), true);
_highMinutesBefore = Param(nameof(HighMinutesBefore), 60);
_highMinutesAfter = Param(nameof(HighMinutesAfter), 60);
_useNfpNews = Param(nameof(UseNfpNews), true);
_nfpMinutesBefore = Param(nameof(NfpMinutesBefore), 180);
_nfpMinutesAfter = Param(nameof(NfpMinutesAfter), 180);
_onlySymbolNews = Param(nameof(OnlySymbolNews), true);
_newsEventsDefinition = Param(nameof(NewsEventsDefinition), string.Empty);
_timeZoneOffsetHours = Param(nameof(TimeZoneOffsetHours), 0);
_takeProfitPoints = Param(nameof(TakeProfitPoints), 100);
_stopLossPoints = Param(nameof(StopLossPoints), 100);
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public bool UseLowNews
{
get => _useLowNews.Value;
set => _useLowNews.Value = value;
}
public int LowMinutesBefore
{
get => _lowMinutesBefore.Value;
set => _lowMinutesBefore.Value = value;
}
public int LowMinutesAfter
{
get => _lowMinutesAfter.Value;
set => _lowMinutesAfter.Value = value;
}
public bool UseMediumNews
{
get => _useMediumNews.Value;
set => _useMediumNews.Value = value;
}
public int MediumMinutesBefore
{
get => _mediumMinutesBefore.Value;
set => _mediumMinutesBefore.Value = value;
}
public int MediumMinutesAfter
{
get => _mediumMinutesAfter.Value;
set => _mediumMinutesAfter.Value = value;
}
public bool UseHighNews
{
get => _useHighNews.Value;
set => _useHighNews.Value = value;
}
public int HighMinutesBefore
{
get => _highMinutesBefore.Value;
set => _highMinutesBefore.Value = value;
}
public int HighMinutesAfter
{
get => _highMinutesAfter.Value;
set => _highMinutesAfter.Value = value;
}
public bool UseNfpNews
{
get => _useNfpNews.Value;
set => _useNfpNews.Value = value;
}
public int NfpMinutesBefore
{
get => _nfpMinutesBefore.Value;
set => _nfpMinutesBefore.Value = value;
}
public int NfpMinutesAfter
{
get => _nfpMinutesAfter.Value;
set => _nfpMinutesAfter.Value = value;
}
public bool OnlySymbolNews
{
get => _onlySymbolNews.Value;
set => _onlySymbolNews.Value = value;
}
public string NewsEventsDefinition
{
get => _newsEventsDefinition.Value;
set => _newsEventsDefinition.Value = value;
}
public int TimeZoneOffsetHours
{
get => _timeZoneOffsetHours.Value;
set => _timeZoneOffsetHours.Value = value;
}
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
private bool HasNewsFilter => UseLowNews || UseMediumNews || UseHighNews || UseNfpNews;
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_previousOpenPrice = null;
_newsEvents.Clear();
_newsBlocking = false;
_lastNewsMessage = string.Empty;
_instrumentCurrencies.Clear();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
BuildInstrumentCurrencies();
ParseNewsEvents();
ConfigureProtection();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ConfigureProtection()
{
// Configure stop-loss and take-profit to mirror the 100 point brackets from the template EA.
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
{
LogWarning("Security step is zero. Protective orders cannot be configured.");
return;
}
var takeUnit = TakeProfitPoints > 0 ? new Unit(step * TakeProfitPoints, UnitTypes.Absolute) : new Unit();
var stopUnit = StopLossPoints > 0 ? new Unit(step * StopLossPoints, UnitTypes.Absolute) : new Unit();
if (TakeProfitPoints > 0 || StopLossPoints > 0)
StartProtection(takeUnit, stopUnit);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
UpdateNewsState(candle.CloseTime);
// No bound indicators to check readiness for.
// Abort any signals while a news blackout is active.
if (_newsBlocking)
{
return;
}
if (_previousOpenPrice == null)
{
// Store the first open price so the next candle can compare against it.
_previousOpenPrice = candle.OpenPrice;
return;
}
var previousOpen = _previousOpenPrice.Value;
_previousOpenPrice = candle.OpenPrice;
if (Volume <= 0)
return;
if (Position != 0)
{
// The original template trades only when there are no existing positions.
return;
}
if (candle.ClosePrice > previousOpen)
{
BuyMarket();
LogInfo($"Long entry after bullish close at {candle.ClosePrice} compared to prior open {previousOpen}.");
}
else if (candle.ClosePrice < previousOpen)
{
SellMarket();
LogInfo($"Short entry after bearish close at {candle.ClosePrice} compared to prior open {previousOpen}.");
}
}
private void UpdateNewsState(DateTimeOffset currentTime)
{
// Without configured events the strategy should allow trading freely.
if (!HasNewsFilter || _newsEvents.Count == 0)
{
if (_newsBlocking)
{
_newsBlocking = false;
NotifyNewsMessage("No upcoming news events.");
}
return;
}
NewsEvent blockingEvent = null;
NewsEvent upcomingEvent = null;
for (var i = 0; i < _newsEvents.Count; i++)
{
var evt = _newsEvents[i];
if (!IsImportanceEnabled(evt.Importance))
continue;
if (!MatchesSecurity(evt))
continue;
if (IsInsideWindow(evt, currentTime))
{
if (blockingEvent == null || evt.Importance > blockingEvent.Importance)
blockingEvent = evt;
}
else if (evt.Time > currentTime)
{
if (upcomingEvent == null || evt.Time < upcomingEvent.Time)
upcomingEvent = evt;
}
}
var wasBlocking = _newsBlocking;
_newsBlocking = blockingEvent != null;
NotifyNewsMessage(BuildNewsMessage(blockingEvent, upcomingEvent));
if (_newsBlocking && !wasBlocking)
CancelActiveOrders();
}
private void NotifyNewsMessage(string message)
{
if (_lastNewsMessage.EqualsIgnoreCase(message))
return;
_lastNewsMessage = message;
LogInfo(message);
}
private bool IsImportanceEnabled(NewsImportances importance)
=> importance switch
{
NewsImportances.Low => UseLowNews,
NewsImportances.Medium => UseMediumNews,
NewsImportances.High => UseHighNews,
NewsImportances.Nfp => UseNfpNews,
_ => false
};
private bool IsInsideWindow(NewsEvent evt, DateTimeOffset currentTime)
{
var before = TimeSpan.FromMinutes(GetMinutesBefore(evt.Importance));
var after = TimeSpan.FromMinutes(GetMinutesAfter(evt.Importance));
var start = evt.Time - before;
var end = evt.Time + after;
return currentTime >= start && currentTime <= end;
}
private int GetMinutesBefore(NewsImportances importance)
=> importance switch
{
NewsImportances.Low => Math.Max(0, LowMinutesBefore),
NewsImportances.Medium => Math.Max(0, MediumMinutesBefore),
NewsImportances.High => Math.Max(0, HighMinutesBefore),
NewsImportances.Nfp => Math.Max(0, NfpMinutesBefore),
_ => 0
};
private int GetMinutesAfter(NewsImportances importance)
=> importance switch
{
NewsImportances.Low => Math.Max(0, LowMinutesAfter),
NewsImportances.Medium => Math.Max(0, MediumMinutesAfter),
NewsImportances.High => Math.Max(0, HighMinutesAfter),
NewsImportances.Nfp => Math.Max(0, NfpMinutesAfter),
_ => 0
};
private string BuildNewsMessage(NewsEvent activeEvent, NewsEvent upcomingEvent)
{
if (activeEvent != null)
{
var label = GetImportanceLabel(activeEvent.Importance);
var timeText = activeEvent.Time.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
var currencyPart = activeEvent.Currency.IsEmptyOrWhiteSpace() ? string.Empty : $" [{activeEvent.Currency}]";
var titlePart = activeEvent.Title.IsEmptyOrWhiteSpace() ? string.Empty : $" - {activeEvent.Title}";
return $"Trading paused due to {label} news{currencyPart} at {timeText}{titlePart}.";
}
if (upcomingEvent != null)
{
var label = GetImportanceLabel(upcomingEvent.Importance);
var timeText = upcomingEvent.Time.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
var currencyPart = upcomingEvent.Currency.IsEmptyOrWhiteSpace() ? string.Empty : $" [{upcomingEvent.Currency}]";
var titlePart = upcomingEvent.Title.IsEmptyOrWhiteSpace() ? string.Empty : $" - {upcomingEvent.Title}";
return $"Next scheduled news: {label}{currencyPart} at {timeText}{titlePart}.";
}
return "No upcoming news events.";
}
private static string GetImportanceLabel(NewsImportances importance)
=> importance switch
{
NewsImportances.Low => "low",
NewsImportances.Medium => "medium",
NewsImportances.High => "high",
NewsImportances.Nfp => "non-farm payroll",
_ => "unknown"
};
private void ParseNewsEvents()
{
// Parse the manual economic calendar description provided in the parameters.
_newsEvents.Clear();
var raw = NewsEventsDefinition;
if (raw.IsEmptyOrWhiteSpace())
{
LogInfo("News events list is empty. The filter will allow trading at all times.");
return;
}
var separators = new[] { ';', '\n', '\r' };
var entries = raw.Split(separators, StringSplitOptions.RemoveEmptyEntries);
for (var entryIndex = 0; entryIndex < entries.Length; entryIndex++)
{
var entry = entries[entryIndex].Trim();
if (entry.Length == 0)
continue;
var rawParts = entry.Split(',');
if (rawParts.Length < 3)
{
LogWarning($"Unable to parse news entry '{entry}'. Expected at least time, currency and importance.");
continue;
}
var parts = new string[rawParts.Length];
for (var i = 0; i < rawParts.Length; i++)
parts[i] = rawParts[i].Trim();
if (!DateTimeOffset.TryParse(parts[0], CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var time))
{
LogWarning($"Unable to parse time '{parts[0]}' in news entry '{entry}'.");
continue;
}
var currencies = parts[1].ToUpperInvariant();
if (!TryParseImportance(parts[2], out var importance))
{
LogWarning($"Unable to parse importance '{parts[2]}' in news entry '{entry}'.");
continue;
}
var title = string.Empty;
if (parts.Length > 3)
{
var count = parts.Length - 3;
var combined = string.Join(",", parts, 3, count);
title = combined.Trim();
}
time = time.ToOffset(TimeSpan.FromHours(TimeZoneOffsetHours));
_newsEvents.Add(new NewsEvent(time, currencies, importance, title));
}
_newsEvents.Sort((left, right) => left.Time.CompareTo(right.Time));
if (_newsEvents.Count > 0)
LogInfo($"Loaded {_newsEvents.Count} manual news event(s).");
else
LogInfo("No valid news events parsed. The filter will remain inactive.");
}
private static bool TryParseImportance(string value, out NewsImportances importance)
{
if (value.IsEmptyOrWhiteSpace())
{
importance = default;
return false;
}
var normalized = value.Trim();
if (normalized.Equals("LOW", StringComparison.OrdinalIgnoreCase))
{
importance = NewsImportances.Low;
return true;
}
if (normalized.Equals("MEDIUM", StringComparison.OrdinalIgnoreCase) ||
normalized.Equals("MID", StringComparison.OrdinalIgnoreCase) ||
normalized.Equals("MIDLE", StringComparison.OrdinalIgnoreCase) ||
normalized.Equals("MODERATE", StringComparison.OrdinalIgnoreCase))
{
importance = NewsImportances.Medium;
return true;
}
if (normalized.Equals("HIGH", StringComparison.OrdinalIgnoreCase))
{
importance = NewsImportances.High;
return true;
}
if (normalized.Equals("NFP", StringComparison.OrdinalIgnoreCase) ||
normalized.Contains("NONFARM", StringComparison.OrdinalIgnoreCase) ||
normalized.Contains("NON-FARM", StringComparison.OrdinalIgnoreCase))
{
importance = NewsImportances.Nfp;
return true;
}
importance = default;
return false;
}
private bool MatchesSecurity(NewsEvent evt)
{
if (!OnlySymbolNews)
return true;
// Match the configured currencies against the current instrument if required.
if (_instrumentCurrencies.Count == 0)
return true;
if (evt.Currency.IsEmptyOrWhiteSpace())
return true;
var separators = new[] { '/', ',', '|', ';', ' ' };
var tokens = evt.Currency.Split(separators, StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < tokens.Length; i++)
{
var token = tokens[i].Trim();
if (token.Length == 0)
continue;
if (_instrumentCurrencies.Contains(token))
return true;
}
return false;
}
private void BuildInstrumentCurrencies()
{
// Extract major currency codes from the security symbol (e.g., EURUSD -> EUR, USD).
_instrumentCurrencies.Clear();
var code = Security?.Code;
if (code.IsEmptyOrWhiteSpace())
return;
var trimmed = code.Trim().ToUpperInvariant();
if (trimmed.Length >= 6)
{
_instrumentCurrencies.Add(trimmed.Substring(0, 3));
_instrumentCurrencies.Add(trimmed.Substring(trimmed.Length - 3, 3));
}
else
{
_instrumentCurrencies.Add(trimmed);
}
}
private sealed record class NewsEvent(DateTimeOffset Time, string Currency, NewsImportances Importance, string Title);
private enum NewsImportances
{
Low = 1,
Medium = 2,
High = 3,
Nfp = 4
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class trade_ea_template_for_news_strategy(Strategy):
"""News template EA: simple candle direction entries with SL/TP via StartProtection."""
def __init__(self):
super(trade_ea_template_for_news_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._take_profit_points = self.Param("TakeProfitPoints", 100) \
.SetDisplay("Take Profit Points", "TP distance in price steps", "Risk")
self._stop_loss_points = self.Param("StopLossPoints", 100) \
.SetDisplay("Stop Loss Points", "SL distance in price steps", "Risk")
self._previous_open_price = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def TakeProfitPoints(self):
return int(self._take_profit_points.Value)
@property
def StopLossPoints(self):
return int(self._stop_loss_points.Value)
def OnStarted2(self, time):
super(trade_ea_template_for_news_strategy, self).OnStarted2(time)
self._previous_open_price = None
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 0.0
if step > 0:
tp_unit = Unit(step * self.TakeProfitPoints, UnitTypes.Absolute) if self.TakeProfitPoints > 0 else None
sl_unit = Unit(step * self.StopLossPoints, UnitTypes.Absolute) if self.StopLossPoints > 0 else None
if tp_unit is not None and sl_unit is not None:
self.StartProtection(takeProfit=tp_unit, stopLoss=sl_unit)
elif tp_unit is not None:
self.StartProtection(takeProfit=tp_unit)
elif sl_unit is not None:
self.StartProtection(stopLoss=sl_unit)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._previous_open_price is None:
self._previous_open_price = float(candle.OpenPrice)
return
previous_open = self._previous_open_price
self._previous_open_price = float(candle.OpenPrice)
if self.Position != 0:
return
close = float(candle.ClosePrice)
if close > previous_open:
self.BuyMarket()
elif close < previous_open:
self.SellMarket()
def OnReseted(self):
super(trade_ea_template_for_news_strategy, self).OnReseted()
self._previous_open_price = None
def CreateClone(self):
return trade_ea_template_for_news_strategy()