Estratégia de Full Candle
A configuração Full Candle entra quando uma vela fecha além de sua EMA e deixa apenas uma sombra pequena no lado do rompimento. A intenção é operar velas de momentum que mostrem ação decisiva sem muito rejeição. Saídas opcionais de take-profit e stop-loss baseadas em porcentagem gerenciam a operação assim que ela está aberta.
O sistema é mais adequado para rompimentos de curto prazo onde velas fortes frequentemente levam a um seguimento rápido.
Detalhes
- Critérios de entrada:
- Comprado: vela altista fechando acima da EMA com sombra ≤ limiar
- Vendido: vela baixista fechando abaixo da EMA com sombra ≤ limiar
- Comprado/Vendido: Ambos
- Critérios de saída:
- Percentuais de take-profit ou stop-loss se habilitados
- Stops: Opcional
- Valores padrão:
EmaLength= 10ShadowPercent= 5TPPercent= 1.2SLPercent= 1.8
- Filtros:
- Categoria: Rompimento
- Direção: Ambos
- Indicadores: EMA, price action
- Stops: Opcional
- Complexidade: Baixo
- Período: Curto prazo
- 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.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Full Candle strategy.
/// Goes long on a bullish candle closing above the EMA whose upper shadow is at most ShadowPercent of the candle range,
/// and short on a bearish candle closing below the EMA whose lower shadow is at most ShadowPercent of the range. The
/// opposite signal reverses the position; percent take-profit and stop-loss manage the trade (0 disables either).
/// </summary>
public class FullCandleStrategy : Strategy
{
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<decimal> _shadowPercent;
private readonly StrategyParam<decimal> _tpPercent;
private readonly StrategyParam<decimal> _slPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Maximum breakout-side shadow in percent of the candle range.
/// </summary>
public decimal ShadowPercent
{
get => _shadowPercent.Value;
set => _shadowPercent.Value = value;
}
/// <summary>
/// Take-profit percentage. 0 disables it.
/// </summary>
public decimal TPPercent
{
get => _tpPercent.Value;
set => _tpPercent.Value = value;
}
/// <summary>
/// Stop-loss percentage. 0 disables it.
/// </summary>
public decimal SLPercent
{
get => _slPercent.Value;
set => _slPercent.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FullCandleStrategy()
{
_emaLength = Param(nameof(EmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Indicators");
_shadowPercent = Param(nameof(ShadowPercent), 5m)
.SetNotNegative()
.SetDisplay("Shadow %", "Maximum breakout-side shadow in percent of the candle range", "Signals");
_tpPercent = Param(nameof(TPPercent), 1.2m)
.SetNotNegative()
.SetDisplay("TP %", "Take-profit percentage, 0 disables", "Risk");
_slPercent = Param(nameof(SLPercent), 1.8m)
.SetNotNegative()
.SetDisplay("SL %", "Stop-loss percentage, 0 disables", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, ProcessCandle)
.Start();
StartProtection(
TPPercent > 0 ? new Unit(TPPercent, UnitTypes.Percent) : new Unit(),
SLPercent > 0 ? new Unit(SLPercent, UnitTypes.Percent) : new Unit(),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!emaValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ema = emaValue.ToDecimal();
var open = candle.OpenPrice;
var close = candle.ClosePrice;
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0)
return;
var maxShadow = range * ShadowPercent / 100m;
var longSignal = close > open && close > ema && candle.HighPrice - close <= maxShadow;
var shortSignal = close < open && close < ema && close - candle.LowPrice <= maxShadow;
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class full_candle_strategy(Strategy):
"""
Full Candle strategy.
Goes long on a bullish candle closing above the EMA whose upper shadow is at most ShadowPercent of the candle range,
and short on a bearish candle closing below the EMA whose lower shadow is at most ShadowPercent of the range. The
opposite signal reverses the position; percent take-profit and stop-loss manage the trade (0 disables either).
"""
def __init__(self):
super(full_candle_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 10) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "EMA period", "Indicators")
self._shadow_percent = self.Param("ShadowPercent", 5.0) \
.SetNotNegative() \
.SetDisplay("Shadow %", "Maximum breakout-side shadow in percent of the candle range", "Signals")
self._tp_percent = self.Param("TPPercent", 1.2) \
.SetNotNegative() \
.SetDisplay("TP %", "Take-profit percentage, 0 disables", "Risk")
self._sl_percent = self.Param("SLPercent", 1.8) \
.SetNotNegative() \
.SetDisplay("SL %", "Stop-loss percentage, 0 disables", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
super(full_candle_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(ema, self._process_candle).Start()
tp = float(self._tp_percent.Value)
sl = float(self._sl_percent.Value)
self.StartProtection(
Unit(Decimal(tp), UnitTypes.Percent) if tp > 0 else Unit(),
Unit(Decimal(sl), UnitTypes.Percent) if sl > 0 else Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not ema_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ema = float(ema_value.GetValue[Decimal](None))
open_price = float(candle.OpenPrice)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
candle_range = high - low
if candle_range <= 0:
return
max_shadow = candle_range * float(self._shadow_percent.Value) / 100.0
long_signal = close > open_price and close > ema and high - close <= max_shadow
short_signal = close < open_price and close < ema and close - low <= max_shadow
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return full_candle_strategy()