The FitFul 13 Time Gated Strategy is a StockSharp port of the MetaTrader 4 expert advisor "FitFul_13". The strategy builds a weekly pivot ladder (PP, R0.5, R1, R1.5, R2, R2.5, R3 and the corresponding support levels) using the previous week's high, low and close. Trade decisions are taken on the primary timeframe (default 1 hour) and are optionally confirmed by a faster timeframe (default 15 minutes). New positions are allowed only at specific intraday minutes to mimic the original EA behaviour.
Signal logic
Weekly pivot calculation
At the close of every weekly candle the pivot ladder is recalculated.
Stop-loss and take-profit prices are offset from the base levels by a configurable distance expressed in price points.
Primary timeframe conditions
The last completed primary candle must be bullish to search for long entries or bearish to search for short entries.
The previous primary candle must straddle one of the pivot levels (open below and close above for longs, open above and close below for shorts).
Confirmation timeframe conditions
If the current confirmation candle is bullish, the lows of the two previous confirmation candles must pierce and close above the same pivot level to confirm a long signal.
If the current confirmation candle is bearish, the highs of the two previous confirmation candles must pierce and close below a pivot level to confirm a short signal.
Entry timing
A trade is placed only when the opening minute of the finished primary candle equals one of the four configured minutes (0, 15, 30 or 45 by default).
Net exposure is capped by MaxNetPositions × Volume to emulate the "maximum three open orders" constraint of the MetaTrader version.
Risk management
Stops and targets – Every position is assigned a pivot-derived stop-loss and take-profit immediately after entry.
Trailing stop – Once price advances by the configured number of points, the stop is trailed in the trade direction.
Maximum holding time – Profitable trades are closed once the holding time exceeds the configured duration (48 hours by default).
Friday flat rule – On Fridays, any open position is closed between the configured minutes of the specified hour (default 21:50–21:59).
Parameters
Name
Description
PrimaryCandleType
Timeframe used for the main pivot cross checks.
ConfirmationCandleType
Faster timeframe that validates pivot reactions.
Volume
Net market order volume.
MaxNetPositions
Maximum exposure measured in multiples of Volume.
OffsetPoints
Price-point distance applied to stops and targets around each pivot.
TrailingStopPoints
Trailing stop distance in price points.
CloseAfter
Maximum holding time for profitable positions.
CloseHour, CloseMinuteFrom, CloseMinuteTo
Friday time window for forced exits.
EntryMinute0..3
Allowed minutes (within every hour) for opening new positions.
Notes
The conversion keeps the original EA's reliance on the previous week's pivot ladder and quarter-hour execution windows.
Money management has been simplified: the StockSharp Volume parameter controls order size directly instead of re-implementing the dynamic lot calculation from MetaTrader.
All comments inside the code are written in English, as required by the project guidelines.
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()