The TCPivotStop Floor Breakout Strategy is a direct port of the MetaTrader expert advisor gpfTCPivotStop. The logic revolves around
classical floor pivot calculations performed on the previous trading day. At the start of every new daily session the strategy:
Aggregates the prior day's high, low, and close to compute the pivot point plus the first three support and resistance tiers.
Checks whether the latest completed hourly bar crossed the pivot from above or below.
Opens a market order in the direction of the crossover while attaching stop-loss and take-profit levels that mirror the
original expert's behaviour.
Only one position can be active at a time. Optional session management allows flattening exposure when a new day begins.
Trading Rules
Timeframe – Designed for 1-hour candles (configurable).
Pivot calculation – Uses the high, low, and close of the previous day to compute Pivot, R1, R2, R3, S1, S2, S3.
Entry conditions
Enter short when the last completed bar closed below the pivot while the preceding bar closed above it.
Enter long when the last completed bar closed above the pivot while the preceding bar closed below it.
Position sizing – Fixed lot size defined by the OrderVolume parameter.
Exit conditions
Stop-loss and take-profit prices are mapped to the classic pivot levels.
If the CloseAtSessionEnd flag is enabled the strategy liquidates open trades before the next session starts.
Protective levels are monitored on candle highs/lows and executed with market orders when touched.
Parameters
Name
Description
Default
OrderVolume
Trade size for market entries.
0.1
TakeProfitTarget
Chooses which pivot tier acts as the profit target (1 = nearest, 3 = farthest).
1
CloseAtSessionEnd
Close any open position once a new daily session begins.
false
CandleType
Timeframe used for all calculations (hourly by default).
H1
Notes
The strategy executes orders only once per day when a new pivot set is available, just like the source EA that triggers on the
first tick of the daily session.
The MetaTrader version recalculated lot sizes using account margin history. This port keeps position sizing fixed and
delegates money management to other components if needed.
Protective orders are emulated by monitoring candle extremes and sending market orders once a threshold is crossed.
Files
CS/TcpFloorPivotBreakoutStrategy.cs – C# implementation of the trading logic.
README.md – English documentation (this file).
README_zh.md – Simplified Chinese translation.
README_ru.md – Russian translation.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class TcpFloorPivotBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
private int _cooldownRemaining;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TcpFloorPivotBreakoutStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 48).SetDisplay("Channel Period", "Channel lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMid = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevMid = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevMid = mid;
return;
}
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_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
from StockSharp.Algo.Strategies import Strategy
class tcp_floor_pivot_breakout_strategy(Strategy):
def __init__(self):
super(tcp_floor_pivot_breakout_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 48).SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 150).SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def channel_period(self): return self._channel_period.Value
@property
def cooldown_candles(self): return self._cooldown_candles.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(tcp_floor_pivot_breakout_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(tcp_floor_pivot_breakout_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_mid = mid
return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return tcp_floor_pivot_breakout_strategy()