Estrategia TradeEATemplateNews
Descripción general
La Estrategia TradeEATemplateNews es una conversión en C# del asesor experto de MetaTrader 4 "Trade EA Template for News". El sistema original pausaba el trading en torno a eventos económicos programados descargados de sitios web externos. Este port de StockSharp mantiene las ideas principales mientras las adapta a la API de alto nivel:
- Usa velas completadas del marco temporal configurado (H1 por defecto).
- Opera sólo cuando la cuenta está plana, exactamente como la plantilla MQL que requería cero órdenes abiertas.
- Aplica una zona de silencio manual de noticias económicas que bloquea entradas antes y después de los eventos según su importancia.
- Crea automáticamente brackets protectores de stop-loss y take-profit a 100 puntos del precio de ejecución (convertidos a través del paso del instrumento).
Lógica de trading
- Cada vela completada activa un recálculo del calendario de noticias. La estrategia almacena el precio de apertura de la vela anterior para que la siguiente barra pueda comparar su cierre con la apertura previa.
- Si el tiempo actual cae dentro de cualquier ventana de silencio configurada, la estrategia cancela órdenes pendientes y no abre nuevas operaciones.
- Cuando no hay posición abierta y el trading está permitido:
- Se abre una posición larga si la última vela cierra por encima del precio de apertura de la vela anterior.
- Se abre una posición corta si la última vela cierra por debajo del precio de apertura de la vela anterior.
- Los niveles de stop-loss y take-profit se expresan en puntos (
TakeProfitPoints y StopLossPoints) y se convierten en desplazamientos de precio absolutos usando el valor Step del instrumento.
Calendario de noticias manual
El experto original descargaba datos de investing.com o DailyFX. Para portabilidad, la versión StockSharp espera un calendario curado manualmente suministrado a través del parámetro NewsEventsDefinition. El formato acepta una lista de entradas separadas por punto y coma o saltos de línea. Cada entrada debe contener al menos tres campos separados por comas:
AAAA-MM-DD HH:MM,DIVISAS,IMPORTANCIA[,TÍTULO]
AAAA-MM-DD HH:MM — inicio del evento en UTC. El parámetro opcional TimeZoneOffsetHours desplaza todos los tiempos parseados por la cantidad solicitada (por ejemplo, establece 3 para UTC+3).
DIVISAS — códigos de divisas o identificadores de instrumentos como USD, EUR, EUR/USD. Múltiples códigos pueden separarse con /, ,, ;, | o espacios.
IMPORTANCIA — palabra clave de importancia. Valores reconocidos: Low, Medium, Mid, Midle, Moderate, High, NFP, cadenas que contengan Nonfarm o Non-farm.
TÍTULO — descripción de texto libre opcional que se imprimirá en los mensajes de registro.
Ejemplo:
2024-03-01 13:30,USD,High,Nonfarm Payrolls;2024-03-01 15:00,USD,Low,Factory Orders
Ventanas de silencio
UseLowNews, UseMediumNews, UseHighNews y UseNfpNews alternan qué eventos se consideran.
LowMinutesBefore/After, MediumMinutesBefore/After, HighMinutesBefore/After y NfpMinutesBefore/After determinan cuántos minutos alrededor del evento se debe deshabilitar el trading.
OnlySymbolNews restringe el silencio a entradas cuyos códigos de divisas coincidan con el instrumento actual (por ejemplo, EURUSD resulta en el par {EUR, USD}). Desactívalo para pausar el trading en cada evento.
- La estrategia mantiene sólo el evento de mayor importancia activo en cualquier momento. Los mensajes de registro informativos anuncian la razón del estado actual y la próxima publicación programada.
Parámetros
| Parámetro |
Descripción |
Predeterminado |
CandleType |
Tipo de datos de velas al que suscribirse. Por defecto 1 hora. |
1h |
UseLowNews |
Habilitar eventos de baja importancia. |
true |
LowMinutesBefore / LowMinutesAfter |
Minutos antes/después de noticias de bajo impacto para bloquear entradas. |
15 / 15 |
UseMediumNews |
Habilitar eventos de importancia media. |
true |
MediumMinutesBefore / MediumMinutesAfter |
Minutos antes/después de noticias de impacto medio. |
30 / 30 |
UseHighNews |
Habilitar eventos de alta importancia. |
true |
HighMinutesBefore / HighMinutesAfter |
Minutos antes/después de noticias de alto impacto. |
60 / 60 |
UseNfpNews |
Habilitar el indicador de Non-farm Payrolls. |
true |
NfpMinutesBefore / NfpMinutesAfter |
Minutos antes/después de eventos NFP. |
180 / 180 |
OnlySymbolNews |
Filtrar el calendario por los códigos de divisas del instrumento actual. |
true |
NewsEventsDefinition |
Cadena de descripción del calendario económico manual. |
vacío |
TimeZoneOffsetHours |
Desplazamiento aplicado a cada evento parseado (UTC por defecto). |
0 |
TakeProfitPoints |
Distancia en puntos para la orden protectora de take-profit. |
100 |
StopLossPoints |
Distancia en puntos para la orden protectora de stop-loss. |
100 |
Volume se hereda de Strategy y debe establecerse según el tamaño de posición deseado.
Diferencias con la versión MQL
- Sin descarga HTTP automática — el usuario suministra la lista de noticias manualmente, lo que evita dependencias externas y mantiene la conversión determinista.
- Las etiquetas de gráfico y líneas verticales se reemplazan con mensajes de registro que describen el evento activo o próximo.
- El experto MQL abría órdenes con tamaño de lote fijo
0.01; en StockSharp el tamaño de posición proviene de la propiedad Volume.
- Toda la lógica se implementa con la API de suscripción de velas de alto nivel preservando el comportamiento consciente de noticias de la plantilla.
Notas de despliegue
- Llena
NewsEventsDefinition antes de iniciar la estrategia o actualízalo, detén y reinicia para recargar el calendario.
- Ajusta
TimeZoneOffsetHours y los parámetros de minutos antes/después para que coincidan con tu sesión de trading.
- Configura
Volume, portafolio e instrumento en la interfaz o en código, luego inicia la estrategia.
- Observa el registro de la estrategia para mensajes como "Trading paused due to high news" o "Next scheduled news" para confirmar la lógica de silencio.
La traducción a Python se omite intencionalmente según lo solicitado.
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()