Esta estratégia replica a lógica do Expert Advisor "CHO Smoothed EA" original. Ele observa os cruzamentos do oscilador Chaikin em velas concluídas e suaviza o oscilador com uma média móvel configurável. Filtros opcionais limitam a negociação a uma sessão específica, restringem a direção da negociação e validam sinais com confirmação de linha zero. Quando um sinal é aceito, a estratégia envia uma ordem de mercado e gerencia a posição usando distâncias fixas em pontos para stop-loss, take-profit e proteção de trailing.
Lógica de negociação
Os valores do oscilador Chaikin são calculados em cada vela finalizada com períodos rápidos e lentos configuráveis.
Uma média móvel do oscilador cria a linha de sinal. O período e o tipo de média móvel podem ser ajustados.
Entradas longas ocorrem quando o oscilador cruza acima da linha suavizada. Entradas curtas ocorrem no cruzamento oposto. Os sinais podem ser revertidos para negociar contra a direção original.
Se o filtro de nível zero estiver habilitado, ambos os valores do oscilador deverão estar abaixo de zero para negociações longas e acima de zero para negociações curtas.
A estratégia pode fechar automaticamente posições opostas antes de entrar em uma nova negociação ou ignorar sinais até que a posição atual fique estável. Também pode impor um modo de posição única.
A negociação pode ser restrita a uma janela de tempo diária. Windows que cruzam a meia-noite são suportados.
Após uma entrada, a estratégia armazena o preço de entrada, converte as distâncias dos pontos configurados em compensações de preço e monitora as velas para eventos de stop-loss, take-profit e trailing-stop.
Gestão de risco
Os níveis de stop-loss e take-profit são calculados a partir do preço de entrada usando distâncias de pontos multiplicadas pela etapa de preço do instrumento.
O trailing stop é ativado após o preço avançar pelo passo final configurado e depois segue na distância final.
Quando um nível de proteção é atingido, a posição é fechada imediatamente com uma ordem de mercado e todos os níveis de risco são redefinidos.
Parâmetros
Tipo de vela – período usado para construir as velas para cálculos de indicadores.
Período Rápido / Período Lento – Períodos rápidos e lentos do Oscilador Chaikin.
Período MA do sinal / Tipo MA do sinal – suavização das configurações de média móvel aplicadas ao oscilador.
Use Nível Zero – exige que ambos os valores do oscilador estejam no lado correto de zero antes de negociar.
Modo de negociação – permite apenas posições compradas, apenas vendidas ou ambas as direções.
Sinais Reversos – troque entradas longas e curtas.
Fechar Oposto – feche posições opostas existentes antes de abrir uma nova negociação.
Apenas uma posição – evita entradas quando uma posição já está aberta.
Use Controle de Tempo / Hora de Início / Hora de Término – habilite e configure a janela de negociação diária.
Stop Loss (pts) – distância em pontos para o stop de proteção.
Take Profit (pts) – distância em pontos para metas de lucro.
Trailing Stop (pts) – distância do trailing stop em pontos.
Trailing Step (pts) – movimento mínimo favorável (em pontos) antes de mover o trailing stop.
Notas adicionais
Defina a propriedade Volume da estratégia antes de iniciá-la para controlar o tamanho da negociação.
Como a estratégia emite ordens de mercado, garanta liquidez suficiente e considere derrapagens em ambientes reais.
Quando os horários de início e término da janela de negociação são iguais, a estratégia permanece inativa, correspondendo ao comportamento original do Expert Advisor.
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>
/// Trades CCI oscillator zero-line crossovers with signal MA smoothing.
/// Originally based on Chaikin Oscillator concept, adapted to use CCI.
/// </summary>
public class ChoSmoothedEaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _maPeriod;
private CommodityChannelIndex _cci;
private readonly Queue<decimal> _cciHistory = new();
private decimal? _prevCci;
private decimal? _prevSignal;
/// <summary>
/// Initializes a new instance of the <see cref="ChoSmoothedEaStrategy"/> class.
/// </summary>
public ChoSmoothedEaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signal calculations", "General");
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Period for CCI oscillator", "Indicator");
_maPeriod = Param(nameof(MaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Signal MA Period", "Period of smoothing moving average on CCI", "Indicator");
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// CCI oscillator period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Period of the smoothing moving average.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = null;
_prevSignal = null;
_cciHistory.Clear();
_cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_cci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
_cciHistory.Enqueue(cciValue);
while (_cciHistory.Count > MaPeriod)
_cciHistory.Dequeue();
if (!_cci.IsFormed)
return;
if (_cciHistory.Count < MaPeriod)
{
_prevCci = cciValue;
return;
}
// Calculate signal line (SMA of CCI)
var sum = 0m;
var history = _cciHistory.ToArray();
foreach (var v in history)
sum += v;
var signalValue = sum / history.Length;
if (_prevCci is null || _prevSignal is null)
{
_prevCci = cciValue;
_prevSignal = signalValue;
return;
}
var crossUp = _prevCci <= _prevSignal && cciValue > signalValue;
var crossDown = _prevCci >= _prevSignal && cciValue < signalValue;
var volume = Volume;
if (volume <= 0)
volume = 1;
var minSpread = 25m;
if (crossUp && Math.Abs(cciValue - signalValue) >= minSpread)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown && Math.Abs(cciValue - signalValue) >= minSpread)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevCci = cciValue;
_prevSignal = signalValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevCci = null;
_prevSignal = null;
_cci = null;
_cciHistory.Clear();
base.OnReseted();
}
}