SurefireThing — это реализация на StockSharp высокоуровневого API экспертной системы MetaTrader 4 Surefirething. Стратегия работает только с закрытыми свечами, вычисляет уровни отложенных заявок по диапазону предыдущей сессии и обнуляет позицию при смене торгового дня. Основная идея заключается в постановке симметричных лимитных заявок вокруг цены закрытия предыдущего дня, чтобы воспользоваться возможным возвратом к среднему.
Логика торговли
При обнаружении нового торгового дня стратегия пытается закрыть открытую позицию и снимает активные отложенные заявки.
Для последней завершённой свечи предыдущего дня рассчитывается диапазон (High - Low) и умножается на RangeMultiplier (по умолчанию 1.1 — как в оригинальном советнике).
Половина скорректированного диапазона прибавляется к цене закрытия для определения уровня sell limit. Та же величина вычитается из цены закрытия для постановки buy limit.
Параметры StopLoss и TakeProfit задаются в шагах цены. Если у инструмента определён Security.Step, значения автоматически переводятся в абсолютные расстояния и обрабатываются через StartProtection, чтобы при исполнении позиций автоматически выставлялись защитные ордера.
Заявки выставляются один раз в сутки. При исполнении выход из позиции происходит за счёт защитных ордеров, иначе лимитные заявки остаются активными до следующей дневной перезагрузки.
Параметры
Параметр
Описание
Значение по умолчанию
OrderVolume
Объём каждой отложенной заявки.
0.1
TakeProfitPoints
Дистанция тейк-профита в шагах цены. При наличии Security.Step переводится в абсолютное значение.
10
StopLossPoints
Дистанция стоп-лосса в шагах цены. Переводится аналогично тейк-профиту.
15
RangeMultiplier
Коэффициент, которым масштабируется диапазон предыдущей свечи.
1.1
CandleType
Основной таймфрейм, используемый стратегией. По умолчанию — минутные свечи, но может быть изменён под исходный график.
TimeSpan.FromMinutes(1)
Особенности реализации
Высокоуровневый API: подписка на свечи осуществляется через SubscribeCandles(CandleType), обработчик ProcessCandle работает только с завершёнными свечами.
Суточный сброс: функция CloseForNewDay следит за сменой календарного дня по времени открытия свечи, закрывает позицию и отменяет заявки.
Защитные ордера: ConfigureProtection преобразует точечные параметры риска в объекты Unit и активирует StartProtection, чтобы стопы и тейки автоматически переустанавливались после сделок.
Управление жизненным циклом ордеров: ссылки на лимитные заявки хранятся в _buyLimitOrder и _sellLimitOrder, очищаются через CancelPendingOrder и OnOrderChanged при завершении или отмене.
Нормализация цен: перед отправкой ордеров цены округляются до шага инструмента функцией Security.ShrinkPrice.
Рекомендации по использованию
Подберите CandleType под таймфрейм, который использовался в оригинальном советнике, чтобы расчёты выполнялись по тем же свечам.
При изменении волатильности инструмента корректируйте RangeMultiplier, чтобы расстояние до лимитных заявок оставалось адекватным.
Если брокер ограничивает минимальные расстояния до стопов, убедитесь, что TakeProfitPoints и StopLossPoints после пересчёта соответствуют правилам площадки.
Стратегия предполагает непрерывные внутридневные данные. При гэпах (выходные, праздники) следующая доступная свеча всё равно инициирует сброс и постановку новых ордеров на основе последнего наблюдаемого бара.
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()