CoensioTrader1 V06 é uma estratégia de seguidor de tendência com rompimento originalmente distribuída como um Expert Advisor do MetaTrader. O port para StockSharp mantém a lógica de reconhecimento de padrões discricionária enquanto remove as funcionalidades específicas do broker e da internet da implementação MQL. A estratégia opera em um único instrumento e período, usando Bollinger Bands e uma média móvel exponencial dupla (DEMA) para identificar movimentos de exaustão seguidos de retomada de tendência.
O robô original permitia negociar até seis pares de moedas com conjuntos de parâmetros individuais, suportava licenciamento baseado em DLL e reportava resultados de otimização para um servidor remoto. Esses serviços auxiliares são omitidos intencionalmente neste port. O foco é o fluxo central de entrada e saída que reage a rejeições de Bollinger Bands confirmadas pela estrutura de swing e pela inclinação do DEMA.
Lógica da estratégia
Subscrição de dados – a estratégia se inscreve no tipo de candle configurado (padrão: 1 hora) e vincula Bollinger Bands juntamente com um DEMA.
Rejeição de Bollinger Bands – os sinais são avaliados no último candle completamente fechado.
Configuração Comprado
O candle abriu abaixo da Bollinger Band inferior anterior e fechou de volta acima dela (rompimento falso para baixo).
O candle criou uma mínima mais alta em comparação com a barra anterior, enquanto essa barra anterior fez uma mínima mais baixa em comparação com seu predecessor (estrutura estilo fundo duplo).
O DEMA está estritamente subindo ao longo das últimas três observações (valor atual > anterior > segundo anterior).
Configuração Vendido
O candle abriu acima da Bollinger Band superior anterior e fechou de volta abaixo dela (rompimento falso para cima).
O candle fez uma máxima mais baixa em comparação com a barra anterior, enquanto essa barra anterior fez uma máxima mais alta em comparação com seu predecessor (estrutura de topo duplo).
O DEMA está estritamente caindo ao longo das últimas três observações.
Execução de ordens – ordens de mercado são enviadas imediatamente após o sinal ser confirmado em um candle terminado. O achatamento opcional de posição em sinais opostos pode ser habilitado.
Gestão de risco – distâncias opcionais de stop-loss e take-profit são fornecidas através de StartProtection. Ambas são offsets de preço absoluto; a funcionalidade de trailing stop do expert original não é reproduzida.
Parâmetros
Nome
Descrição
Padrão
BollingerPeriod
Período para o cálculo das Bollinger Bands.
30
BollingerDeviation
Multiplicador de desvio padrão para as bandas.
1.5
DemaPeriod
Comprimento da média móvel exponencial dupla usada para confirmação de tendência.
20
StopLossDistance
Offset absoluto de stop-loss passado para StartProtection. Definir como zero para desabilitar.
0 (absolute)
TakeProfitDistance
Offset absoluto de take-profit passado para StartProtection. Definir como zero para desabilitar.
0 (absolute)
CloseOnSignal
Fechar a posição atual antes de abrir uma nova na direção oposta.
false
CandleType
Tipo de dado de candle ou período. Padrão é o período de 1 hora.
1h
Notas de uso
A versão StockSharp negocia apenas o Strategy.Security principal. Para imitar o comportamento multi-símbolo do expert original, lance instâncias de estratégia separadas com conjuntos de parâmetros distintos.
A lógica de dimensionamento de lotes MQL (RiskMax, LotSize, LotBalanceDivider) não foi traduzida. Configure Volume na estratégia ou via gerenciador de risco de acordo com as regras do seu portfólio.
A ativação baseada em DLL, o registro remoto de otimização e as rotinas de desenho de UI presentes nos arquivos MQL foram intencionalmente removidos.
Os valores de stop-loss e take-profit são distâncias de preço absolutas. Adapte-os ao tamanho do tick ou valor de pip do instrumento ao configurar a estratégia.
O mecanismo original de passo de trailing-stop não está implementado. Se o gerenciamento de trailing for necessário, adicione um módulo de risco dedicado sobre esta estratégia.
Todos os comentários do código e lógicas são mantidos em inglês conforme solicitado; as traduções do README são fornecidas separadamente.
Diferenças em relação à versão MQL
Gestão multi-símbolo: substituída por um design de instrumento único para maior clareza e testes mais fáceis.
Redes e licenciamento: removidos; nenhuma solicitação HTTP externa ou chamada de DLL é realizada.
Dimensionamento de ordens: simplificado para depender do tratamento padrão de Volume do StockSharp.
Objetos visuais: anotações de gráfico do MetaTrader (setas, rótulos, temas de cor) não são recriados. Use os helpers de gráfico do StockSharp se visualização for necessária.
Trailing stop: não portado; apenas as ordens protetoras iniciais são configuradas.
Esta documentação pretende ser exaustiva para que o port possa ser revisado, testado e estendido sem necessidade de ler o código fonte MQL original.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the CoensioTrader1 V06 MQL strategy.
/// The strategy buys after a lower Bollinger Band rejection paired with a higher low pattern and bullish DEMA trend.
/// It sells after an upper Bollinger Band rejection with a lower high structure and bearish DEMA trend.
/// </summary>
public class CoensioTrader1V06Strategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _demaPeriod;
private readonly StrategyParam<Unit> _stopLossDistance;
private readonly StrategyParam<Unit> _takeProfitDistance;
private readonly StrategyParam<bool> _closeOnSignal;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevOpen;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevClose;
private decimal? _prev2High;
private decimal? _prev2Low;
private decimal? _prev3High;
private decimal? _prev3Low;
private decimal? _prevUpperBand;
private decimal? _prevLowerBand;
private decimal? _prevDema;
private decimal? _prev2Dema;
/// <summary>
/// Initializes a new instance of the <see cref="CoensioTrader1V06Strategy"/> class.
/// </summary>
public CoensioTrader1V06Strategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Length of Bollinger Bands", "Indicators")
;
_bollingerDeviation = Param(nameof(BollingerDeviation), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier", "Indicators")
;
_demaPeriod = Param(nameof(DemaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("DEMA Period", "Length of double exponential moving average", "Indicators")
;
_stopLossDistance = Param(nameof(StopLossDistance), new Unit(0m, UnitTypes.Absolute))
.SetDisplay("Stop Loss", "Absolute stop loss offset from entry", "Risk");
_takeProfitDistance = Param(nameof(TakeProfitDistance), new Unit(0m, UnitTypes.Absolute))
.SetDisplay("Take Profit", "Absolute take profit offset from entry", "Risk");
_closeOnSignal = Param(nameof(CloseOnSignal), false)
.SetDisplay("Close On Opposite Signal", "Close current trades when opposite setup appears", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame for signal calculations", "General");
Volume = 0.01m;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Double exponential moving average period.
/// </summary>
public int DemaPeriod
{
get => _demaPeriod.Value;
set => _demaPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance from entry price.
/// </summary>
public Unit StopLossDistance
{
get => _stopLossDistance.Value;
set => _stopLossDistance.Value = value;
}
/// <summary>
/// Take-profit distance from entry price.
/// </summary>
public Unit TakeProfitDistance
{
get => _takeProfitDistance.Value;
set => _takeProfitDistance.Value = value;
}
/// <summary>
/// Close current position when an opposite signal appears.
/// </summary>
public bool CloseOnSignal
{
get => _closeOnSignal.Value;
set => _closeOnSignal.Value = value;
}
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevOpen = null;
_prevHigh = null;
_prevLow = null;
_prevClose = null;
_prev2High = null;
_prev2Low = null;
_prev3High = null;
_prev3Low = null;
_prevUpperBand = null;
_prevLowerBand = null;
_prevDema = null;
_prev2Dema = null;
}
/// <inheritdoc />
private BollingerBands _bollinger;
private DoubleExponentialMovingAverage _dema;
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
_dema = new DoubleExponentialMovingAverage
{
Length = DemaPeriod
};
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 bollingerResult = _bollinger.Process(candle);
var demaResult = _dema.Process(candle);
if (bollingerResult.IsEmpty || demaResult.IsEmpty || !_bollinger.IsFormed || !_dema.IsFormed)
{
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_prevLow = candle.LowPrice;
_prevHigh = candle.HighPrice;
return;
}
var bollingerValue = (BollingerBandsValue)bollingerResult;
var upper = bollingerValue.UpBand ?? 0m;
var lower = bollingerValue.LowBand ?? 0m;
var demaValue = demaResult.GetValue<decimal>();
if (_prevOpen.HasValue && _prevClose.HasValue && _prevLow.HasValue && _prevHigh.HasValue &&
_prevLowerBand.HasValue && _prevUpperBand.HasValue && _prevDema.HasValue)
{
// Long setup: lower band rejection with rising DEMA.
var crossedLowerBand = _prevLow.Value <= _prevLowerBand.Value && _prevClose.Value > _prevLowerBand.Value;
var bullishTrend = demaValue > _prevDema.Value;
if (crossedLowerBand && bullishTrend)
{
if (CloseOnSignal && Position < 0)
{
if (Position > 0) SellMarket(Position);
else if (Position < 0) BuyMarket(-Position);
}
if (Position <= 0)
BuyMarket();
}
// Short setup: upper band rejection with falling DEMA.
var crossedUpperBand = _prevHigh.Value >= _prevUpperBand.Value && _prevClose.Value < _prevUpperBand.Value;
var bearishTrend = demaValue < _prevDema.Value;
if (crossedUpperBand && bearishTrend)
{
if (CloseOnSignal && Position > 0)
{
if (Position > 0) SellMarket(Position);
else if (Position < 0) BuyMarket(-Position);
}
if (Position >= 0)
SellMarket();
}
}
_prev3Low = _prev2Low;
_prev3High = _prev2High;
_prev2Low = _prevLow;
_prev2High = _prevHigh;
_prevLowerBand = lower;
_prevUpperBand = upper;
_prev2Dema = _prevDema;
_prevDema = demaValue;
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_prevLow = candle.LowPrice;
_prevHigh = candle.HighPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Indicators import BollingerBands, DoubleExponentialMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan
class coensio_trader1_v06_strategy(Strategy):
def __init__(self):
super(coensio_trader1_v06_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 30)
self._bollinger_deviation = self.Param("BollingerDeviation", 1.5)
self._dema_period = self.Param("DemaPeriod", 20)
self._close_on_signal = self.Param("CloseOnSignal", False)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_open = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._prev_upper_band = None
self._prev_lower_band = None
self._prev_dema = None
self._bollinger = None
self._dema = None
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(coensio_trader1_v06_strategy, self).OnStarted2(time)
self._bollinger = BollingerBands()
self._bollinger.Length = self._bollinger_period.Value
self._bollinger.Width = self._bollinger_deviation.Value
self._dema = DoubleExponentialMovingAverage()
self._dema.Length = self._dema_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
civ1 = CandleIndicatorValue(self._bollinger, candle)
civ1.IsFinal = True
bb_result = self._bollinger.Process(civ1)
civ2 = CandleIndicatorValue(self._dema, candle)
civ2.IsFinal = True
dema_result = self._dema.Process(civ2)
if bb_result.IsEmpty or dema_result.IsEmpty or not self._bollinger.IsFormed or not self._dema.IsFormed:
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_low = float(candle.LowPrice)
self._prev_high = float(candle.HighPrice)
return
upper = float(bb_result.UpBand) if bb_result.UpBand is not None else 0.0
lower = float(bb_result.LowBand) if bb_result.LowBand is not None else 0.0
dema_value = float(dema_result)
if (self._prev_open is not None and self._prev_close is not None and
self._prev_low is not None and self._prev_high is not None and
self._prev_lower_band is not None and self._prev_upper_band is not None and
self._prev_dema is not None):
crossed_lower = self._prev_low <= self._prev_lower_band and self._prev_close > self._prev_lower_band
bullish_trend = dema_value > self._prev_dema
if crossed_lower and bullish_trend:
if self._close_on_signal.Value and self.Position < 0:
self.BuyMarket(abs(self.Position))
if self.Position <= 0:
self.BuyMarket()
crossed_upper = self._prev_high >= self._prev_upper_band and self._prev_close < self._prev_upper_band
bearish_trend = dema_value < self._prev_dema
if crossed_upper and bearish_trend:
if self._close_on_signal.Value and self.Position > 0:
self.SellMarket(self.Position)
if self.Position >= 0:
self.SellMarket()
self._prev_lower_band = lower
self._prev_upper_band = upper
self._prev_dema = dema_value
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_low = float(candle.LowPrice)
self._prev_high = float(candle.HighPrice)
def OnReseted(self):
super(coensio_trader1_v06_strategy, self).OnReseted()
self._prev_open = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._prev_upper_band = None
self._prev_lower_band = None
self._prev_dema = None
def CreateClone(self):
return coensio_trader1_v06_strategy()