Auf GitHub ansehen

E-Friday-Strategie

Übersicht

  • Konvertiert den ursprünglichen MetaTrader-Expertenberater E-Friday.mq5 in die StockSharp High-Level-API.
  • Handelt nur, wenn der Chart-Zeitrahmen H1 oder kürzer ist; andernfalls protokolliert die Strategie eine Warnung und bleibt flat.
  • Einstieg in Positionen auf kontrarische Weise: eine bärische Kerze eröffnet eine Long-Position und eine bullische Kerze eröffnet eine Short-Position.
  • Deaktiviert das Trading jeden Freitag vollständig, um das ursprüngliche Wochenendschutzverhalten zu übernehmen.
  • Beschränkt das Trading auf ein konfigurierbares Zeitfenster und kann Positionen nach Sitzungsende zwangsschließen.

Handelslogik

  1. Bei jeder abgeschlossenen Kerze prüft die Strategie die aktuelle Börsenzeit:
    • wenn der Tag Freitag ist, wird jede Aktion übersprungen;
    • wenn die Stunde vor der konfigurierten Startstunde liegt, wird gewartet;
    • wenn das Schließfenster aktiviert ist und die Stunde über der Endstunde liegt, werden alle Positionen geschlossen und neue Eintritte übersprungen.
  2. Wenn Handel erlaubt ist, steuert die letzte abgeschlossene Kerze das Signal:
    • wenn Open > Close (bärischer Körper) bereitet die Strategie einen Long-Einstieg vor;
    • wenn Open < Close (bullischer Körper) bereitet die Strategie einen Short-Einstieg vor;
    • gleiche Eröffnungs- und Schlusskurse heben alle ausstehenden Aktionen auf.
  3. Vor dem Einstieg in eine neue Position wird die aktuelle Exposition geschlossen, sodass niemals mehr als eine Nettoposition besteht.

Positionsmanagement

  • Lotgröße – aus TradeVolume übernommen und an BuyMarket / SellMarket-Orders gesendet.
  • Stop-Loss und Take-Profit – in Pips gemessen. Pips werden aus Security.PriceStep berechnet und mit 10 multipliziert, wenn das Instrument 3 oder 5 Dezimalstellen hat, genau wie in der MQL-Version.
  • Trailing-Stop – aktiviert sich, sobald der Preis TrailingStopPips + TrailingStepPips zugunsten der Position wechselt. Der Stop wird auf aktueller Preis - Trailing-Stop (Long) oder aktueller Preis + Trailing-Stop (Short) nachgezogen.
  • Ausstiege werden anhand von Kerzenextrema bewertet:
    • eine Long-Position schließt, wenn das Kerzentief den Stop berührt oder das Hoch das Take-Profit erreicht;
    • eine Short-Position schließt, wenn das Kerzenhoch den Stop berührt oder das Tief das Take-Profit erreicht.
  • Nach der Sitzungsendezeit (wenn UseCloseHour = true) werden alle offenen Positionen über Marktorders geschlossen.

Parameter

Name Beschreibung
CandleType Zeitrahmen der verarbeiteten Kerzen. Muss einen positiven TimeSpan definieren und sollte eine Stunde nicht überschreiten.
TradeVolume Ordervolumen in Lots. Muss positiv sein.
StopLossPips Abstand vom Eintrittspreis zum Schutz-Stop in Pips. Null zum Deaktivieren des anfänglichen Stops.
TakeProfitPips Abstand vom Eintrittspreis zum Gewinnziel in Pips. Null zum Deaktivieren des Ziels.
TrailingStopPips Trailing-Stop-Abstand in Pips. Funktioniert zusammen mit TrailingStepPips.
TrailingStepPips Minimaler zusätzlicher Fortschritt (in Pips) bevor der Trailing-Stop nachgezogen wird. Muss positiv sein, wenn der Trailing-Stop aktiviert ist.
StartHour Stunde (Börsenzeit), ab der die Strategie mit der Positionseröffnung beginnen darf.
UseCloseHour Aktiviert oder deaktiviert das Zwangsschließen nach der Endstunde.
EndHour Stunde (Börsenzeit), nach der die Strategie aufhört zu handeln und bestehende Positionen schließt.

Implementierungshinweise

  • Verwendet SubscribeCandles und die High-Level-Bind-API, damit Indikatoren später bei Bedarf hinzugefügt werden können.
  • Validiert die Trailing-Konfiguration beim Start: wenn ein Trailing-Stop angefordert wird, muss der Trailing-Schritt strikt positiv sein.
  • Die Pip-Konvertierung spiegelt die ursprüngliche EA-Logik (PriceStep * 10 für 3/5-stellige Symbole) wider, um Stop-Loss-Abstände konsistent zu halten.
  • Die StockSharp-Version bewertet Stops und Ziele einmal pro abgeschlossener Kerze. Der ursprüngliche EA lief auf jedem Tick, daher kann der StockSharp-Port einige Ticks später aussteigen, aber die Logik bleibt gleichwertig.
  • Die Strategie ruft CloseActivePosition explizit auf, wenn das Sitzungsfenster endet. Das MQL-Skript enthielt dieselbe Idee, kehrte jedoch zurück, bevor die Schließroutine erreicht wurde; die C#-Version implementiert das beabsichtigte Verhalten.
  • Informative Protokolle (AddInfoLog / AddWarningLog) werden verwendet, um übersprungene Handelszeiträume in der Benutzeroberfläche anzuzeigen.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// E-Friday strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class EFridayStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public EFridayStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}