Estratégia de Rompimento Diário
Esta estratégia opera rompimentos da abertura diária. No início de cada novo dia, o preço de abertura é armazenado. Quando o preço se afasta deste nível por um número de pontos definido pelo utilizador, e a barra anterior está dentro de um intervalo de tamanho configurável, a estratégia entra na direção do rompimento.
Lógica de entrada
- Se a barra anterior for de alta e o preço subir acima da abertura diária em Break Point pontos, uma posição comprada é aberta.
- Se a barra anterior for de baixa e o preço cair abaixo da abertura diária em Break Point pontos, uma posição vendida é aberta.
- O tamanho da barra anterior deve estar entre Last Bar Min e Last Bar Max pontos.
- O nível de rompimento deve estar dentro do corpo da barra anterior.
Gestão de risco
- O Take Profit e Stop Loss opcionais são medidos em pontos a partir do preço de entrada.
- Um trailing stop pode ser ativado com os parâmetros Trailing Start, Trailing Stop e Trailing Step. Quando o preço se move favoravelmente em Trailing Start pontos, o stop é definido em Trailing Stop pontos da entrada e então segue em incrementos de Trailing Step.
Parâmetros
| Nome | Descrição |
|---|---|
| Candle Type | Período das velas processadas. |
| Break Point | Distância da abertura diária para acionar um trade (pontos). |
| Last Bar Min | Tamanho mínimo da barra anterior (pontos). |
| Last Bar Max | Tamanho máximo da barra anterior (pontos). |
| Trailing Start | Movimento de preço para iniciar o trailing stop (pontos). |
| Trailing Stop | Distância inicial do trailing stop (pontos). |
| Trailing Step | Passo para mover o trailing stop (pontos). |
| Take Profit | Distância de take profit (pontos). |
| Stop Loss | Distância de stop loss (pontos). |
Notas
A estratégia opera apenas em velas concluídas e utiliza ordens a mercado para entradas e saídas. Armazena variáveis internas para os dados da barra anterior e o nível do trailing stop.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Daily-open breakout in security price steps, gated by the preceding
/// completed candle's body. Fixed protection uses actual fills and the optional
/// trailing exit is evaluated only on subsequent completed candles.
/// </summary>
public class DailyBreakpointStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _breakPoint, _lastBarMin, _lastBarMax;
private readonly StrategyParam<decimal> _takeProfit, _stopLoss;
private readonly StrategyParam<decimal> _trailingStart, _trailingStop, _trailingStep;
private decimal _dailyOpen, _previousOpen, _previousClose;
private DateTimeOffset _previousTime;
private bool _hasPrevious;
private decimal? _entryPrice, _bestPrice, _trailPrice;
private decimal _trackedPosition;
private Order _pending;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal BreakPoint { get => _breakPoint.Value; set => _breakPoint.Value = value; }
public decimal LastBarMin { get => _lastBarMin.Value; set => _lastBarMin.Value = value; }
public decimal LastBarMax { get => _lastBarMax.Value; set => _lastBarMax.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal TrailingStart { get => _trailingStart.Value; set => _trailingStart.Value = value; }
public decimal TrailingStop { get => _trailingStop.Value; set => _trailingStop.Value = value; }
public decimal TrailingStep { get => _trailingStep.Value; set => _trailingStep.Value = value; }
public DailyBreakpointStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Signal timeframe", "General");
_breakPoint = Param(nameof(BreakPoint), 30m).SetNotNegative()
.SetDisplay("Break Point", "Daily-open breakout offset in price steps", "Trading");
_lastBarMin = Param(nameof(LastBarMin), 5m).SetNotNegative()
.SetDisplay("Last Bar Min", "Minimum previous candle body in price steps", "Trading");
_lastBarMax = Param(nameof(LastBarMax), 100m).SetNotNegative()
.SetDisplay("Last Bar Max", "Maximum previous candle body in price steps", "Trading");
_takeProfit = Param(nameof(TakeProfit), 200m).SetNotNegative()
.SetDisplay("Take Profit", "Optional target in price steps", "Risk");
_stopLoss = Param(nameof(StopLoss), 100m).SetNotNegative()
.SetDisplay("Stop Loss", "Optional risk in price steps", "Risk");
_trailingStart = Param(nameof(TrailingStart), 0m).SetNotNegative()
.SetDisplay("Trailing Start", "Favorable move activating trailing in steps", "Risk");
_trailingStop = Param(nameof(TrailingStop), 0m).SetNotNegative()
.SetDisplay("Trailing Stop", "Trail distance; zero disables trailing", "Risk");
_trailingStep = Param(nameof(TrailingStep), 1m).SetGreaterThanZero()
.SetDisplay("Trailing Step", "Minimum improvement before a trail update", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
private void ResetState()
{
_dailyOpen = _previousOpen = _previousClose = 0m;
_previousTime = default;
_hasPrevious = false;
_entryPrice = _bestPrice = _trailPrice = null;
_trackedPosition = 0m;
_pending = null;
}
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var step = Security.PriceStep is decimal valid && valid > 0m ? valid : 1m;
StartProtection(
takeProfit: new Unit(TakeProfit * step, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss * step, UnitTypes.Absolute),
useMarketOrders: true);
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
protected override void OnNewPosition(Position position)
{
base.OnNewPosition(position);
ObservePosition(position);
}
protected override void OnPositionChanged(Position position)
{
base.OnPositionChanged(position);
ObservePosition(position);
}
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
ObservePosition(position);
}
private void ObservePosition(Position position)
{
if (position.Security != Security || position.Portfolio != Portfolio) return;
var volume = position.CurrentValue ?? 0m;
if (volume == 0m)
{
_entryPrice = _bestPrice = _trailPrice = null;
_trackedPosition = 0m;
return;
}
if (position.AveragePrice is not decimal avg || avg <= 0m) return;
if (_entryPrice == avg && _trackedPosition * volume > 0m)
{
_trackedPosition = volume;
return;
}
_entryPrice = _bestPrice = avg;
_trailPrice = null;
_trackedPosition = volume;
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished) return;
// The daily open is pinned to the very first completed candle of that day.
var isNewDay = !_hasPrevious || candle.OpenTime.Date != _previousTime.Date;
if (isNewDay) _dailyOpen = candle.OpenPrice;
var step = Security.PriceStep is decimal valid && valid > 0m ? valid : 1m;
var price = candle.ClosePrice;
if (IsFormedAndOnlineAndAllowTrading() &&
(_pending is null || _pending.State is OrderStates.Done or OrderStates.Failed))
{
if (Position != 0m && _entryPrice is decimal entry && TrailingStop > 0m)
{
var isLong = Position > 0m;
var best = _bestPrice ?? entry;
best = isLong ? Math.Max(best, price) : Math.Min(best, price);
_bestPrice = best;
if ((isLong ? best - entry : entry - best) >= TrailingStart * step)
{
var candidate = isLong ? best - TrailingStop * step : best + TrailingStop * step;
if (_trailPrice is not decimal current ||
(isLong ? candidate >= current + TrailingStep * step
: candidate <= current - TrailingStep * step))
_trailPrice = candidate;
}
if (_trailPrice is decimal trail && (isLong ? price <= trail : price >= trail))
_pending = isLong ? SellMarket(Math.Abs(Position)) : BuyMarket(Math.Abs(Position));
}
else if (Position == 0m && !isNewDay && _hasPrevious && _previousTime.Date == candle.OpenTime.Date)
{
var range = Math.Abs(_previousClose - _previousOpen);
var offset = BreakPoint * step;
var buyBreak = _dailyOpen + offset;
var sellBreak = _dailyOpen - offset;
var validSize = range >= LastBarMin * step && range <= LastBarMax * step;
if (validSize && _previousClose > _previousOpen && price > buyBreak &&
buyBreak >= _previousOpen && buyBreak <= _previousClose)
_pending = BuyMarket(Volume);
else if (validSize && _previousClose < _previousOpen && price < sellBreak &&
sellBreak <= _previousOpen && sellBreak >= _previousClose)
_pending = SellMarket(Volume);
}
}
_previousOpen = candle.OpenPrice;
_previousClose = candle.ClosePrice;
_previousTime = candle.OpenTime;
_hasPrevious = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, OrderStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class daily_breakpoint_strategy(Strategy):
"""Daily-open breakout with previous candle body and price-step protections."""
def __init__(self):
super(daily_breakpoint_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay(
"Candle Type", "Signal timeframe", "General")
self._break_point = self.Param("BreakPoint", 30.0).SetNotNegative().SetDisplay(
"Break Point", "Daily-open breakout offset in price steps", "Trading")
self._last_bar_min = self.Param("LastBarMin", 5.0).SetNotNegative().SetDisplay(
"Last Bar Min", "Minimum previous candle body in steps", "Trading")
self._last_bar_max = self.Param("LastBarMax", 100.0).SetNotNegative().SetDisplay(
"Last Bar Max", "Maximum previous candle body in steps", "Trading")
self._take_profit = self.Param("TakeProfit", 200.0).SetNotNegative().SetDisplay(
"Take Profit", "Optional target in price steps", "Risk")
self._stop_loss = self.Param("StopLoss", 100.0).SetNotNegative().SetDisplay(
"Stop Loss", "Optional risk in price steps", "Risk")
self._trailing_start = self.Param("TrailingStart", 0.0).SetNotNegative().SetDisplay(
"Trailing Start", "Activation gain in price steps", "Risk")
self._trailing_stop = self.Param("TrailingStop", 0.0).SetNotNegative().SetDisplay(
"Trailing Stop", "Trail distance; zero disables trailing", "Risk")
self._trailing_step = self.Param("TrailingStep", 1.0).SetGreaterThanZero().SetDisplay(
"Trailing Step", "Minimum improvement to adjust trailing", "Risk")
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_state(self):
self._daily_open = self._previous_open = self._previous_close = 0.0
self._previous_time = None
self._has_previous = False
self._entry_price = self._best_price = self._trail_price = None
self._tracked_position = 0.0
self._pending = None
def OnReseted(self):
super(daily_breakpoint_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(daily_breakpoint_strategy, self).OnStarted2(time)
self._reset_state()
step = float(self.Security.PriceStep or 1)
if step <= 0:
step = 1
self.StartProtection(
takeProfit=Unit(float(self._take_profit.Value) * step, UnitTypes.Absolute),
stopLoss=Unit(float(self._stop_loss.Value) * step, UnitTypes.Absolute),
useMarketOrders=True)
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def OnNewPosition(self, position):
super(daily_breakpoint_strategy, self).OnNewPosition(position)
self._observe_position(position)
def OnPositionChanged(self, position):
super(daily_breakpoint_strategy, self).OnPositionChanged(position)
self._observe_position(position)
def OnPositionReceived(self, position):
super(daily_breakpoint_strategy, self).OnPositionReceived(position)
self._observe_position(position)
def _observe_position(self, position):
if position.Security != self.Security or position.Portfolio != self.Portfolio:
return
volume = float(position.CurrentValue or 0)
if volume == 0:
self._entry_price = self._best_price = self._trail_price = None
self._tracked_position = 0.0
return
if position.AveragePrice is None or position.AveragePrice <= 0:
return
avg = float(position.AveragePrice)
if self._entry_price == avg and self._tracked_position * volume > 0:
self._tracked_position = volume
return
self._entry_price = self._best_price = avg
self._trail_price = None
self._tracked_position = volume
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
new_day = not self._has_previous or candle.OpenTime.Date != self._previous_time.Date
if new_day:
self._daily_open = float(candle.OpenPrice)
price = float(candle.ClosePrice)
step = float(self.Security.PriceStep or 1)
if step <= 0:
step = 1
can_order = self.IsFormedAndOnlineAndAllowTrading() and (
self._pending is None or self._pending.State in (OrderStates.Done, OrderStates.Failed))
if can_order:
pos = float(self.Position)
if pos != 0 and self._entry_price is not None and float(self._trailing_stop.Value) > 0:
entry = self._entry_price
is_long = pos > 0
best = self._best_price if self._best_price is not None else entry
best = max(best, price) if is_long else min(best, price)
self._best_price = best
if (best - entry if is_long else entry - best) >= float(self._trailing_start.Value) * step:
candidate = best - float(self._trailing_stop.Value) * step if is_long else best + float(self._trailing_stop.Value) * step
current = self._trail_price
movement = float(self._trailing_step.Value) * step
if current is None or (candidate >= current + movement if is_long else candidate <= current - movement):
self._trail_price = candidate
if self._trail_price is not None and (price <= self._trail_price if is_long else price >= self._trail_price):
self._pending = self.SellMarket(abs(self.Position)) if is_long else self.BuyMarket(abs(self.Position))
elif pos == 0 and not new_day and self._has_previous and self._previous_time.Date == candle.OpenTime.Date:
body = abs(self._previous_close - self._previous_open)
offset = float(self._break_point.Value) * step
buy_level = self._daily_open + offset
sell_level = self._daily_open - offset
valid_size = float(self._last_bar_min.Value) * step <= body <= float(self._last_bar_max.Value) * step
if valid_size and self._previous_close > self._previous_open and price > buy_level and self._previous_open <= buy_level <= self._previous_close:
self._pending = self.BuyMarket(self.Volume)
elif valid_size and self._previous_close < self._previous_open and price < sell_level and self._previous_close <= sell_level <= self._previous_open:
self._pending = self.SellMarket(self.Volume)
self._previous_open = float(candle.OpenPrice)
self._previous_close = price
self._previous_time = candle.OpenTime
self._has_previous = True
def CreateClone(self):
return daily_breakpoint_strategy()