A estratégia SurefireThing é uma versão StockSharp de alto nível do consultor especialista MetaTrader 4 Surefirething. Ele opera em velas concluídas, calcula os níveis de ordens pendentes do intervalo da sessão anterior e redefine a exposição na virada de cada dia de negociação. A lógica está centrada na implantação de um par simétrico de ordens limitadas que tentam capturar a reversão à média em torno do fechamento anterior.
Lógica de negociação
No final de cada dia de negociação, a estratégia tenta nivelar a posição e cancela quaisquer ordens pendentes ativas.
Usando a última vela concluída do dia anterior, ele mede a faixa de preço (High - Low) e multiplica-a por RangeMultiplier (o padrão é 1,1 como no EA original).
Metade do intervalo ajustado é adicionado ao fechamento anterior para obter o preço de entrada com limite de venda. A mesma distância é subtraída do fechamento para colocar a ordem com limite de compra.
As compensações de stop-loss e take-profit são expressas em etapas de preços. Quando o instrumento expõe um Security.Step válido, eles são convertidos em distâncias absolutas e gerenciados por meio de StartProtection para que as posições preenchidas recebam ordens de proteção automaticamente.
As ordens são enviadas uma vez por dia de negociação. Se ocorrerem preenchimentos, a proteção anexada trata das saídas; caso contrário, os pedidos permanecerão ativos até a próxima reinicialização diária.
Parâmetros
Parâmetro
Descrição
Padrão
OrderVolume
Volume enviado com cada ordem pendente.
0.1
TakeProfitPoints
Distância (em etapas de preço) para a meta de lucro. Convertido em um deslocamento absoluto quando a etapa é conhecida.
10
StopLossPoints
Distância (em etapas de preço) para o stop de proteção. Convertido da mesma forma que a meta de lucro.
15
RangeMultiplier
Fator aplicado ao intervalo de velas anterior antes de calcular os preços de entrada.
1.1
CandleType
Período primário processado pela estratégia. O padrão é velas de 1 minuto, mas pode ser ajustado para corresponder ao gráfico original.
TimeSpan.FromMinutes(1)
Notas de implementação
API de alto nível: as velas são consumidas por meio de SubscribeCandles(CandleType) e processadas no manipulador ProcessCandle assim que são concluídas.
Redefinição diária: CloseForNewDay cancela ordens pendentes e fecha posições sempre que um novo dia de calendário é detectado a partir de carimbos de data e hora de velas.
Lógica de proteção: ConfigureProtection traduz os controles de risco baseados em pontos em Unit instâncias e ativa StartProtection para que as ordens stop-loss e take-profit sejam recriadas automaticamente após o preenchimento.
Ciclo de vida do pedido: as referências a ambos os pedidos pendentes são armazenadas e limpas via CancelPendingOrder, bem como OnOrderChanged quando os pedidos são concluídos ou cancelados.
Normalização de preços: Security.ShrinkPrice é usado para arredondar os preços calculados para o tamanho do tick do instrumento antes de enviar novos pedidos.
Recomendações de uso
Alinhe CandleType com o período de tempo usado pelo EA original (normalmente o gráfico onde foi anexado) para manter as mesmas velas de referência.
Ajuste RangeMultiplier quando os instrumentos apresentam diferentes características de volatilidade para que as ordens pendentes permaneçam dentro de distâncias realistas.
Se o corretor impor distâncias mínimas de parada, certifique-se de que TakeProfitPoints e StopLossPoints respeitem essas restrições após a conversão para preços absolutos.
A estratégia pressupõe dados intradiários contínuos. Quando ocorrem grandes lacunas (fins de semana, feriados), a próxima vela disponível ainda aciona um reset e uma nova colocação de pedido com base na última barra observada.
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()