The SurefireThing strategy is a StockSharp high-level port of the MetaTrader 4 expert advisor Surefirething. It operates on completed candles, computes pending order levels from the previous session range, and resets exposure at the turn of each trading day. The logic is centred on deploying a symmetrical pair of limit orders that attempt to capture mean reversion around the prior close.
Trading Logic
At the close of every trading day the strategy attempts to flatten the position and cancels any active pending orders.
Using the last completed candle of the previous day it measures the price range (High - Low) and multiplies it by RangeMultiplier (defaults to 1.1 as in the original EA).
Half of the adjusted range is added to the previous close to obtain the sell-limit entry price. The same distance is subtracted from the close to place the buy-limit order.
Stop-loss and take-profit offsets are expressed in price steps. When the instrument exposes a valid Security.Step, they are converted to absolute distances and managed through StartProtection so that filled positions receive protective orders automatically.
Orders are submitted once per trading day. If fills occur, the attached protection handles exits; otherwise orders remain active until the next daily reset.
Parameters
Parameter
Description
Default
OrderVolume
Volume submitted with each pending order.
0.1
TakeProfitPoints
Distance (in price steps) for the profit target. Converted to an absolute offset when the step is known.
10
StopLossPoints
Distance (in price steps) for the protective stop. Converted the same way as the profit target.
15
RangeMultiplier
Factor applied to the previous candle range before computing entry prices.
1.1
CandleType
Primary timeframe processed by the strategy. Defaults to 1-minute candles but can be adjusted to match the original chart.
TimeSpan.FromMinutes(1)
Implementation Notes
High-level API: candles are consumed through SubscribeCandles(CandleType) and processed in the ProcessCandle handler once they are finished.
Daily reset: CloseForNewDay cancels pending orders and closes positions whenever a new calendar day is detected from candle timestamps.
Protective logic: ConfigureProtection translates the point-based risk controls into Unit instances and activates StartProtection so that stop-loss and take-profit orders are automatically recreated after fills.
Order lifecycle: references to both pending orders are stored and cleared via CancelPendingOrder as well as OnOrderChanged when the orders finish or are cancelled.
Price normalisation: Security.ShrinkPrice is used to round computed prices to the instrument tick size before submitting new orders.
Usage Recommendations
Align CandleType with the timeframe used by the original EA (typically the chart where it was attached) to maintain the same reference candles.
Adjust RangeMultiplier when instruments display different volatility characteristics so that the pending orders remain within realistic distances.
If the broker enforces minimum stop distances, make sure TakeProfitPoints and StopLossPoints respect those constraints after conversion to absolute prices.
The strategy assumes continuous intraday data. When large gaps occur (weekends, holidays), the next available candle still triggers a reset and new order placement based on the last observed bar.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Daily range breakout strategy.
/// Calculates buy/sell levels from previous day's range.
/// Buys when price drops below the lower level, sells when above the upper level.
/// Closes position at end of day.
/// </summary>
public class SurefireThingStrategy : Strategy
{
private readonly StrategyParam<decimal> _rangeMultiplier;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _currentDay;
private decimal _buyLevel;
private decimal _sellLevel;
private bool _levelsReady;
private decimal _prevDayClose;
private decimal _prevDayHigh;
private decimal _prevDayLow;
private decimal _dayHigh;
private decimal _dayLow;
private decimal _dayClose;
private bool _hasPrevDay;
private bool _tradedToday;
public decimal RangeMultiplier
{
get => _rangeMultiplier.Value;
set => _rangeMultiplier.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public SurefireThingStrategy()
{
_rangeMultiplier = Param(nameof(RangeMultiplier), 0.5m)
.SetDisplay("Range Mult", "Multiplier for range-based levels", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentDay = null;
_buyLevel = 0m;
_sellLevel = 0m;
_levelsReady = false;
_prevDayClose = 0m;
_prevDayHigh = 0m;
_prevDayLow = 0m;
_dayHigh = 0m;
_dayLow = 0m;
_dayClose = 0m;
_hasPrevDay = false;
_tradedToday = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_currentDay = null;
_levelsReady = false;
_hasPrevDay = false;
_tradedToday = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
// New day detected
if (_currentDay == null || day > _currentDay.Value)
{
// Close position at end of previous day
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
// Save previous day stats
if (_currentDay != null)
{
_prevDayClose = _dayClose;
_prevDayHigh = _dayHigh;
_prevDayLow = _dayLow;
_hasPrevDay = true;
}
// Calculate new levels
if (_hasPrevDay)
{
var range = _prevDayHigh - _prevDayLow;
if (range > 0)
{
var halfRange = range * RangeMultiplier;
_buyLevel = _prevDayClose - halfRange;
_sellLevel = _prevDayClose + halfRange;
_levelsReady = true;
}
}
_currentDay = day;
_dayHigh = candle.HighPrice;
_dayLow = candle.LowPrice;
_dayClose = candle.ClosePrice;
_tradedToday = false;
}
else
{
if (candle.HighPrice > _dayHigh) _dayHigh = candle.HighPrice;
if (candle.LowPrice < _dayLow) _dayLow = candle.LowPrice;
_dayClose = candle.ClosePrice;
}
if (!_levelsReady)
return;
var price = candle.ClosePrice;
// Only one trade per day per direction
if (!_tradedToday && Position == 0)
{
if (price <= _buyLevel)
{
BuyMarket();
_tradedToday = true;
}
else if (price >= _sellLevel)
{
SellMarket();
_tradedToday = true;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class surefire_thing_strategy(Strategy):
"""Daily range breakout strategy. Calculates buy/sell levels from previous day's range.
Buys when price drops below the lower level, sells when above the upper level.
Closes position at end of day."""
def __init__(self):
super(surefire_thing_strategy, self).__init__()
self._range_multiplier = self.Param("RangeMultiplier", 0.5) \
.SetDisplay("Range Mult", "Multiplier for range-based levels", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle series", "General")
self._current_day = None
self._buy_level = 0.0
self._sell_level = 0.0
self._levels_ready = False
self._prev_day_close = 0.0
self._prev_day_high = 0.0
self._prev_day_low = 0.0
self._day_high = 0.0
self._day_low = 0.0
self._day_close = 0.0
self._has_prev_day = False
self._traded_today = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RangeMultiplier(self):
return self._range_multiplier.Value
def OnReseted(self):
super(surefire_thing_strategy, self).OnReseted()
self._current_day = None
self._buy_level = 0.0
self._sell_level = 0.0
self._levels_ready = False
self._prev_day_close = 0.0
self._prev_day_high = 0.0
self._prev_day_low = 0.0
self._day_high = 0.0
self._day_low = 0.0
self._day_close = 0.0
self._has_prev_day = False
self._traded_today = False
def OnStarted2(self, time):
super(surefire_thing_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# New day detected
if self._current_day is None or day > self._current_day:
# Close position at end of previous day
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
# Save previous day stats
if self._current_day is not None:
self._prev_day_close = self._day_close
self._prev_day_high = self._day_high
self._prev_day_low = self._day_low
self._has_prev_day = True
# Calculate new levels
if self._has_prev_day:
day_range = self._prev_day_high - self._prev_day_low
if day_range > 0:
half_range = day_range * float(self.RangeMultiplier)
self._buy_level = self._prev_day_close - half_range
self._sell_level = self._prev_day_close + half_range
self._levels_ready = True
self._current_day = day
self._day_high = high
self._day_low = low
self._day_close = close
self._traded_today = False
else:
if high > self._day_high:
self._day_high = high
if low < self._day_low:
self._day_low = low
self._day_close = close
if not self._levels_ready:
return
# Only one trade per day
if not self._traded_today and self.Position == 0:
if close <= self._buy_level:
self.BuyMarket()
self._traded_today = True
elif close >= self._sell_level:
self.SellMarket()
self._traded_today = True
def CreateClone(self):
return surefire_thing_strategy()