Die FitFul 13 Time Gated Strategy ist eine StockSharp-Portierung des MetaTrader 4-Expertenberaters „FitFul_13“. Die Strategie erstellt eine wöchentliche Pivot-Leiter (PP, R0,5, R1, R1,5, R2, R2,5, R3 und die entsprechenden Unterstützungsniveaus) unter Verwendung der Höchst-, Tiefst- und Schlusskurse der Vorwoche. Handelsentscheidungen werden im primären Zeitrahmen (Standard 1 Stunde) getroffen und optional durch einen schnelleren Zeitrahmen (Standard 15 Minuten) bestätigt. Neue Positionen sind nur zu bestimmten Intraday-Minuten zulässig, um das ursprüngliche EA-Verhalten nachzuahmen.
Signallogik
Wöchentliche Pivot-Berechnung
Am Ende jeder wöchentlichen Kerze wird die Pivot-Leiter neu berechnet.
Stop-Loss- und Take-Profit-Preise werden von den Basisniveaus um einen konfigurierbaren Abstand, ausgedrückt in Preispunkten, versetzt.
Primäre Zeitrahmenbedingungen
Die letzte abgeschlossene Primärkerze muss bullisch sein, um nach Long-Einstiegen zu suchen, oder bärisch, um nach Short-Einstiegen zu suchen.
Die vorherige Primärkerze muss eine der Pivot-Ebenen überspannen (unten öffnen und oben schließen für Long-Positionen, oben öffnen und unten schließen für Short-Positionen).
Bedingungen für den Bestätigungszeitraum
Wenn die aktuelle Bestätigungskerze bullisch ist, müssen die Tiefs der beiden vorherigen Bestätigungskerzen das gleiche Pivot-Level durchbrechen und darüber schließen, um ein Long-Signal zu bestätigen.
Wenn die aktuelle Bestätigungskerze bärisch ist, müssen die Höchststände der beiden vorherigen Bestätigungskerzen ein Pivot-Level durchbrechen und darunter schließen, um ein Short-Signal zu bestätigen.
Eintrittszeitpunkt
Ein Handel wird nur dann platziert, wenn die Eröffnungsminute der fertigen Primärkerze einer der vier konfigurierten Minuten entspricht (standardmäßig 0, 15, 30 oder 45).
Das Nettorisiko ist auf MaxNetPositions × Volume begrenzt, um die Beschränkung „maximal drei offene Bestellungen“ der Version MetaTrader zu emulieren.
Risikomanagement
Stopps und Ziele – Jeder Position wird unmittelbar nach dem Einstieg ein vom Pivot abgeleiteter Stop-Loss und Take-Profit zugewiesen.
Trailing Stop – Sobald der Preis um die konfigurierte Anzahl von Punkten steigt, wird der Stop in Handelsrichtung nachgezogen.
Maximale Haltezeit – Profitable Geschäfte werden geschlossen, sobald die Haltezeit die konfigurierte Dauer überschreitet (standardmäßig 48 Stunden).
Freitags-Flat-Regel – Freitags wird jede offene Position zwischen den konfigurierten Minuten der angegebenen Stunde (Standard 21:50–21:59) geschlossen.
Parameter
Name
Beschreibung
PrimaryCandleType
Zeitrahmen, der für die Haupt-Pivot-Gegenprüfungen verwendet wird.
ConfirmationCandleType
Schnellerer Zeitrahmen, der Pivot-Reaktionen validiert.
Volume
Netto-Marktauftragsvolumen.
MaxNetPositions
Maximale Belichtung, gemessen in Vielfachen von Volume.
OffsetPoints
Preispunktabstand, der auf Stopps und Ziele um jeden Drehpunkt angewendet wird.
TrailingStopPoints
Trailing-Stop-Distanz in Preispunkten.
CloseAfter
Maximale Haltezeit für profitable Positionen.
CloseHour, CloseMinuteFrom, CloseMinuteTo
Freitags-Zeitfenster für erzwungene Ausgänge.
EntryMinute0..3
Erlaubte Minuten (innerhalb jeder Stunde) für die Eröffnung neuer Positionen.
Notizen
Die Konvertierung behält die ursprüngliche Abhängigkeit von EA von der Pivot-Leiter und den viertelstündigen Ausführungsfenstern der Vorwoche bei.
Die Geldverwaltung wurde vereinfacht: Der Parameter StockSharp Volume steuert die Auftragsgröße direkt, anstatt die dynamische Losberechnung von MetaTrader erneut zu implementieren.
Alle Kommentare im Code sind gemäß den Projektrichtlinien auf Englisch verfasst.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// FitFul 13 Time Gated strategy - channel midpoint crossover.
/// Buys when close crosses above the midpoint of Highest/Lowest channel.
/// Sells when close crosses below the midpoint.
/// </summary>
public class FitFul13TimeGatedStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FitFul13TimeGatedStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 13)
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 13)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevMid = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ema, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev)
{
_prevClose = close;
_prevMid = mid;
_hasPrev = true;
return;
}
// Cross above midpoint with EMA confirmation
if (_prevClose <= _prevMid && close > mid && close > ema && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Cross below midpoint with EMA confirmation
else if (_prevClose >= _prevMid && close < mid && close < ema && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevClose = close;
_prevMid = mid;
}
}
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 Highest, Lowest, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fit_ful13_time_gated_strategy(Strategy):
def __init__(self):
super(fit_ful13_time_gated_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 13) \
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators")
self._ema_period = self.Param("EmaPeriod", 13) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
@property
def channel_period(self):
return self._channel_period.Value
@property
def ema_period(self):
return self._ema_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fit_ful13_time_gated_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(fit_ful13_time_gated_strategy, self).OnStarted2(time)
self._has_prev = False
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, ema, self.process_candle).Start()
def process_candle(self, candle, highest, lowest, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
high_val = float(highest)
low_val = float(lowest)
ema_val = float(ema)
mid = (high_val + low_val) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._prev_close <= self._prev_mid and close > mid and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_mid and close < mid and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return fit_ful13_time_gated_strategy()