TradeEATemplateNews-Strategie
Übersicht
Die TradeEATemplateNews-Strategie ist eine C#-Konvertierung des MetaTrader 4 Expert Advisors "Trade EA Template for News". Das ursprüngliche System pausierte den Handel rund um geplante wirtschaftliche Ereignisse, die von externen Websites heruntergeladen wurden. Dieser StockSharp-Port behält die Kernideen bei und passt sie an die High-Level-API an:
- Verwendet abgeschlossene Kerzen des konfigurierten Zeitrahmens (H1 standardmäßig).
- Handelt nur, wenn das Konto flat ist, genau wie die MQL-Vorlage, die null offene Orders erforderte.
- Wendet eine manuelle wirtschaftliche Nachrichtensperre an, die Einstiege vor und nach Ereignissen je nach ihrer Wichtigkeit blockiert.
- Erstellt automatisch schützende Stop-Loss- und Take-Profit-Brackets 100 Punkte vom Ausführungspreis entfernt (konvertiert über den Wertpapier-Schritt).
Handelslogik
- Jede abgeschlossene Kerze löst eine Neuberechnung des Nachrichtenplans aus. Die Strategie speichert den Eröffnungspreis der vorherigen Kerze, damit die nächste Bar ihren Schluss mit dem vorherigen Eröffnungspreis vergleichen kann.
- Wenn die aktuelle Zeit in ein konfiguriertes Sperrzeitfenster fällt, storniert die Strategie ausstehende Orders und öffnet keine neuen Trades.
- Wenn keine Position offen ist und der Handel erlaubt ist:
- Eine Long-Position wird eröffnet, wenn die letzte Kerze über dem Eröffnungspreis der vorherigen Kerze schließt.
- Eine Short-Position wird eröffnet, wenn die letzte Kerze unter dem Eröffnungspreis der vorherigen Kerze schließt.
- Stop-Loss- und Take-Profit-Levels werden in Punkten ausgedrückt (
TakeProfitPoints und StopLossPoints) und über den Step-Wert des Wertpapiers in absolute Preisverschiebungen umgewandelt.
Manueller Nachrichtenplan
Der ursprüngliche Experte lud Daten von investing.com oder DailyFX herunter. Für Portabilität erwartet die StockSharp-Version einen manuell kuratierten Kalender, der über den Parameter NewsEventsDefinition bereitgestellt wird. Das Format akzeptiert eine Liste von Einträgen, getrennt durch Semikolons oder Zeilenumbrüche. Jeder Eintrag muss mindestens drei kommagetrennte Felder enthalten:
JJJJ-MM-TT HH:MM,WÄHRUNGEN,WICHTIGKEIT[,TITEL]
JJJJ-MM-TT HH:MM — Ereignisstart in UTC. Der optionale Parameter TimeZoneOffsetHours verschiebt alle geparsten Zeiten um den angeforderten Betrag (setze zum Beispiel 3 für UTC+3).
WÄHRUNGEN — Währungscodes oder Instrumentenidentifikatoren wie USD, EUR, EUR/USD. Mehrere Codes können mit /, ,, ;, | oder Leerzeichen getrennt werden.
WICHTIGKEIT — Wichtigkeits-Schlüsselwort. Erkannte Werte: Low, Medium, Mid, Midle, Moderate, High, NFP, Strings, die Nonfarm oder Non-farm enthalten.
TITEL — optionale Freitextbeschreibung, die in Protokollnachrichten gedruckt wird.
Beispiel:
2024-03-01 13:30,USD,High,Nonfarm Payrolls;2024-03-01 15:00,USD,Low,Factory Orders
Sperrzeitfenster
UseLowNews, UseMediumNews, UseHighNews und UseNfpNews schalten um, welche Ereignisse berücksichtigt werden.
LowMinutesBefore/After, MediumMinutesBefore/After, HighMinutesBefore/After und NfpMinutesBefore/After bestimmen, wie viele Minuten rund um das Ereignis der Handel deaktiviert werden soll.
OnlySymbolNews beschränkt die Sperre auf Einträge, deren Währungscodes mit dem aktuellen Wertpapier übereinstimmen (z. B. führt EURUSD zum Paar {EUR, USD}). Deaktiviere es, um den Handel bei jedem Ereignis zu pausieren.
- Die Strategie hält zu jedem Zeitpunkt nur das Ereignis mit der höchsten Wichtigkeit aktiv. Informationsprotokollnachrichten kündigen den Grund für den aktuellen Status und die nächste geplante Veröffentlichung an.
Parameter
| Parameter |
Beschreibung |
Standard |
CandleType |
Kerzen-Datentyp zum Abonnieren. Standardmäßig 1 Stunde. |
1h |
UseLowNews |
Ereignisse mit geringer Wichtigkeit aktivieren. |
true |
LowMinutesBefore / LowMinutesAfter |
Minuten vor/nach Nachrichten mit geringer Auswirkung zum Blockieren von Einstiegen. |
15 / 15 |
UseMediumNews |
Ereignisse mit mittlerer Wichtigkeit aktivieren. |
true |
MediumMinutesBefore / MediumMinutesAfter |
Minuten vor/nach Nachrichten mit mittlerer Auswirkung. |
30 / 30 |
UseHighNews |
Ereignisse mit hoher Wichtigkeit aktivieren. |
true |
HighMinutesBefore / HighMinutesAfter |
Minuten vor/nach Nachrichten mit hoher Auswirkung. |
60 / 60 |
UseNfpNews |
Das Non-farm Payrolls-Flag aktivieren. |
true |
NfpMinutesBefore / NfpMinutesAfter |
Minuten vor/nach NFP-Ereignissen. |
180 / 180 |
OnlySymbolNews |
Den Kalender nach den Währungscodes des aktuellen Wertpapiers filtern. |
true |
NewsEventsDefinition |
Manuelle wirtschaftliche Kalender-Beschreibungszeichenkette. |
leer |
TimeZoneOffsetHours |
Versatz, der auf jedes geparste Ereignis angewendet wird (UTC standardmäßig). |
0 |
TakeProfitPoints |
Abstand in Punkten für die schützende Take-Profit-Order. |
100 |
StopLossPoints |
Abstand in Punkten für die schützende Stop-Loss-Order. |
100 |
Volume wird von Strategy geerbt und sollte gemäß der gewünschten Positionsgröße gesetzt werden.
Unterschiede zur MQL-Version
- Kein automatischer HTTP-Download — der Benutzer liefert die Nachrichtenliste manuell, was externe Abhängigkeiten vermeidet und die Konvertierung deterministisch hält.
- Chart-Labels und vertikale Linien werden durch Protokollnachrichten ersetzt, die das aktive oder bevorstehende Ereignis beschreiben.
- Der MQL-Experte öffnete Orders mit fester Losgröße
0.01; in StockSharp kommt die Positionsgröße aus der Volume-Eigenschaft.
- Die gesamte Logik ist mit der High-Level-Kerzenabonnement-API implementiert, während das nachrichtenbewusste Verhalten der Vorlage beibehalten wird.
Bereitstellungshinweise
- Fülle
NewsEventsDefinition vor dem Start der Strategie aus oder aktualisiere es, stoppe und starte neu, um den Plan neu zu laden.
- Passe
TimeZoneOffsetHours und die Minuten-vor/nach-Parameter an deine Handelssitzung an.
- Setze
Volume, Portfolio und Wertpapier in der Benutzeroberfläche oder im Code, dann starte die Strategie.
- Beobachte das Strategieprotokoll auf Nachrichten wie "Trading paused due to high news" oder "Next scheduled news", um die Sperrlogik zu bestätigen.
Die Python-Übersetzung wird absichtlich weggelassen, wie angefordert.
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()