Estratégia de Barra Externa
Esta estratégia opera rompimentos de barras externas. Uma barra externa de alta ocorre quando a máxima da vela atual está acima da máxima anterior e sua mínima está abaixo da mínima anterior. As ordens são colocadas dentro da barra com tomada de lucro parcial opcional e movimento do stop para o ponto de equilíbrio.
Detalhes
- Critérios de entrada: Barra externa com classificação de alta ou baixa.
- Comprado/Vendido: Ambos.
- Critérios de saída: Stop-loss ou take-profit derivados do intervalo da barra.
- Stops: Sim.
- Valores padrão:
CandleType= 5 minuteEntryPercentage= 0.5TpPercentage= 1PartialRR= 1PartialExitPercent= 0.5StopLossOffset= 10
- Filtros:
- Categoria: Padrão
- Direção: Ambos
- Indicadores: Candlestick
- Stops: Sim
- Complexidade: Intermediário
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Outside bar strategy.
/// A candle whose high is above the previous high and whose low is below the previous low is an outside bar, bullish when it closes
/// above its open and bearish otherwise. An entry is placed inside the bar, EntryPercentage of its range back from the extreme in the
/// signal direction. The stop sits StopLossOffset price steps beyond the opposite extreme and the target TpPercentage of the range
/// from the entry. At PartialRR times the risk PartialExitPercent of the position is closed and the stop moves to breakeven.
/// </summary>
public class OutsideBarStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _entryPercentage;
private readonly StrategyParam<decimal> _tpPercentage;
private readonly StrategyParam<decimal> _partialRr;
private readonly StrategyParam<decimal> _partialExitPercent;
private readonly StrategyParam<decimal> _stopLossOffset;
private decimal? _prevHigh;
private decimal? _prevLow;
private int _pendingDirection;
private decimal _pendingEntry;
private decimal _pendingStop;
private decimal _pendingTake;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
private decimal _partialPrice;
private bool _partialDone;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Fraction of the bar range the entry sits back from the extreme in the signal direction.
/// </summary>
public decimal EntryPercentage
{
get => _entryPercentage.Value;
set => _entryPercentage.Value = value;
}
/// <summary>
/// Take profit distance as a fraction of the bar range.
/// </summary>
public decimal TpPercentage
{
get => _tpPercentage.Value;
set => _tpPercentage.Value = value;
}
/// <summary>
/// Reward to risk ratio of the partial exit.
/// </summary>
public decimal PartialRR
{
get => _partialRr.Value;
set => _partialRr.Value = value;
}
/// <summary>
/// Fraction of the position closed at the partial exit.
/// </summary>
public decimal PartialExitPercent
{
get => _partialExitPercent.Value;
set => _partialExitPercent.Value = value;
}
/// <summary>
/// Stop distance beyond the bar in price steps.
/// </summary>
public decimal StopLossOffset
{
get => _stopLossOffset.Value;
set => _stopLossOffset.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public OutsideBarStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_entryPercentage = Param(nameof(EntryPercentage), 0.5m)
.SetRange(0m, 1m)
.SetDisplay("Entry %", "Fraction of the bar range the entry sits back from the extreme", "Trading");
_tpPercentage = Param(nameof(TpPercentage), 1m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit distance as a fraction of the bar range", "Risk");
_partialRr = Param(nameof(PartialRR), 1m)
.SetGreaterThanZero()
.SetDisplay("Partial RR", "Reward to risk ratio of the partial exit", "Risk");
_partialExitPercent = Param(nameof(PartialExitPercent), 0.5m)
.SetRange(0m, 1m)
.SetDisplay("Partial Exit %", "Fraction of the position closed at the partial exit", "Risk");
_stopLossOffset = Param(nameof(StopLossOffset), 10m)
.SetNotNegative()
.SetDisplay("Stop Offset", "Stop distance beyond the bar in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevHigh = null;
_prevLow = null;
_pendingDirection = 0;
_pendingEntry = 0m;
_pendingStop = 0m;
_pendingTake = 0m;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_partialPrice = 0m;
_partialDone = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
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 prevHigh = _prevHigh;
var prevLow = _prevLow;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
ManagePosition(candle);
return;
}
if (_pendingDirection != 0 && TryFillPending(candle))
return;
if (prevHigh is not decimal ph || prevLow is not decimal pl)
return;
if (candle.HighPrice <= ph || candle.LowPrice >= pl)
return;
var range = candle.HighPrice - candle.LowPrice;
var offset = StopLossOffset * (Security?.PriceStep ?? 1m);
// A newer outside bar replaces any unfilled entry.
if (candle.ClosePrice > candle.OpenPrice)
{
_pendingDirection = 1;
_pendingEntry = candle.HighPrice - range * EntryPercentage;
_pendingStop = candle.LowPrice - offset;
_pendingTake = _pendingEntry + range * TpPercentage;
}
else
{
_pendingDirection = -1;
_pendingEntry = candle.LowPrice + range * EntryPercentage;
_pendingStop = candle.HighPrice + offset;
_pendingTake = _pendingEntry - range * TpPercentage;
}
}
private bool TryFillPending(ICandleMessage candle)
{
if (candle.LowPrice > _pendingEntry || candle.HighPrice < _pendingEntry)
return false;
var risk = Math.Abs(_pendingEntry - _pendingStop);
_entryPrice = _pendingEntry;
_stopPrice = _pendingStop;
_takePrice = _pendingTake;
_partialPrice = _pendingDirection > 0 ? _entryPrice + risk * PartialRR : _entryPrice - risk * PartialRR;
_partialDone = false;
if (_pendingDirection > 0)
BuyMarket(Volume);
else
SellMarket(Volume);
_pendingDirection = 0;
return true;
}
private void ManagePosition(ICandleMessage candle)
{
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
{
SellMarket(Position);
return;
}
if (!_partialDone && candle.HighPrice >= _partialPrice)
{
_partialDone = true;
_stopPrice = _entryPrice;
var part = Position * PartialExitPercent;
if (part > 0)
SellMarket(Math.Min(part, Position));
}
}
else
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
{
BuyMarket(-Position);
return;
}
if (!_partialDone && candle.LowPrice <= _partialPrice)
{
_partialDone = true;
_stopPrice = _entryPrice;
var part = -Position * PartialExitPercent;
if (part > 0)
BuyMarket(Math.Min(part, -Position));
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class outside_bar_strategy(Strategy):
"""
Outside bar strategy.
A candle whose high is above the previous high and whose low is below the previous low is an outside bar, bullish when it closes
above its open and bearish otherwise. An entry is placed inside the bar, EntryPercentage of its range back from the extreme in the
signal direction. The stop sits StopLossOffset price steps beyond the opposite extreme and the target TpPercentage of the range
from the entry. At PartialRR times the risk PartialExitPercent of the position is closed and the stop moves to breakeven.
"""
def __init__(self):
super(outside_bar_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._entry_percentage = self.Param("EntryPercentage", 0.5).SetRange(0.0, 1.0).SetDisplay("Entry %", "Fraction of the bar range the entry sits back from the extreme", "Trading")
self._tp_percentage = self.Param("TpPercentage", 1.0).SetGreaterThanZero().SetDisplay("Take Profit %", "Take profit distance as a fraction of the bar range", "Risk")
self._partial_rr = self.Param("PartialRR", 1.0).SetGreaterThanZero().SetDisplay("Partial RR", "Reward to risk ratio of the partial exit", "Risk")
self._partial_exit_percent = self.Param("PartialExitPercent", 0.5).SetRange(0.0, 1.0).SetDisplay("Partial Exit %", "Fraction of the position closed at the partial exit", "Risk")
self._stop_loss_offset = self.Param("StopLossOffset", 10.0).SetNotNegative().SetDisplay("Stop Offset", "Stop distance beyond the bar in price steps", "Risk")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_high = None
self._prev_low = None
self._pending_direction = 0
self._pending_entry = Decimal(0)
self._pending_stop = Decimal(0)
self._pending_take = Decimal(0)
self._entry_price = Decimal(0)
self._stop_price = Decimal(0)
self._take_price = Decimal(0)
self._partial_price = Decimal(0)
self._partial_done = False
def OnReseted(self):
super(outside_bar_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(outside_bar_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
prev_high = self._prev_high
prev_low = self._prev_low
self._prev_high = candle.HighPrice
self._prev_low = candle.LowPrice
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
self._manage_position(candle)
return
if self._pending_direction != 0 and self._try_fill_pending(candle):
return
if prev_high is None or prev_low is None:
return
if candle.HighPrice <= prev_high or candle.LowPrice >= prev_low:
return
rng = candle.HighPrice - candle.LowPrice
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
offset = Decimal(self._stop_loss_offset.Value) * step
entry_pct = Decimal(self._entry_percentage.Value)
tp_pct = Decimal(self._tp_percentage.Value)
# A newer outside bar replaces any unfilled entry.
if candle.ClosePrice > candle.OpenPrice:
self._pending_direction = 1
self._pending_entry = candle.HighPrice - rng * entry_pct
self._pending_stop = candle.LowPrice - offset
self._pending_take = self._pending_entry + rng * tp_pct
else:
self._pending_direction = -1
self._pending_entry = candle.LowPrice + rng * entry_pct
self._pending_stop = candle.HighPrice + offset
self._pending_take = self._pending_entry - rng * tp_pct
def _try_fill_pending(self, candle):
if candle.LowPrice > self._pending_entry or candle.HighPrice < self._pending_entry:
return False
risk = abs(self._pending_entry - self._pending_stop)
partial_rr = Decimal(self._partial_rr.Value)
self._entry_price = self._pending_entry
self._stop_price = self._pending_stop
self._take_price = self._pending_take
if self._pending_direction > 0:
self._partial_price = self._entry_price + risk * partial_rr
else:
self._partial_price = self._entry_price - risk * partial_rr
self._partial_done = False
if self._pending_direction > 0:
self.BuyMarket(self.Volume)
else:
self.SellMarket(self.Volume)
self._pending_direction = 0
return True
def _manage_position(self, candle):
exit_pct = Decimal(self._partial_exit_percent.Value)
if self.Position > 0:
if candle.LowPrice <= self._stop_price or candle.HighPrice >= self._take_price:
self.SellMarket(self.Position)
return
if not self._partial_done and candle.HighPrice >= self._partial_price:
self._partial_done = True
self._stop_price = self._entry_price
part = self.Position * exit_pct
if part > 0:
self.SellMarket(min(part, self.Position))
else:
if candle.HighPrice >= self._stop_price or candle.LowPrice <= self._take_price:
self.BuyMarket(-self.Position)
return
if not self._partial_done and candle.LowPrice <= self._partial_price:
self._partial_done = True
self._stop_price = self._entry_price
part = -self.Position * exit_pct
if part > 0:
self.BuyMarket(min(part, -self.Position))
def CreateClone(self):
return outside_bar_strategy()