Стратегия KWAN CCC повторяет работу MetaTrader-советника Exp_KWAN_CCC.mq5, используя высокоуровневый API StockSharp. Торговые сигналы формируются на основе пользовательского осциллятора:
Рассчитывается осциллятор Chaikin (разница между быстрой и медленной скользящими средними линии накопления/распределения).
Значение Chaikin умножается на Commodity Channel Index (CCI).
Результат делится на значение Momentum. При нулевом Momentum используется постоянное значение 100, как и в оригинале, чтобы избежать деления на ноль.
Полученный ряд сглаживается выбранным методом XMA.
Когда цвет меняется с 0 на любое другое значение, стратегия закрывает шорт и открывает лонг. Когда цвет меняется с 2 на любое другое значение, закрывается лонг и открывается шорт. Логика полностью соответствует MQL-версии и поддерживает смещение сигнала (SignalBar).
Торговые правила
Вход в лонг: цвет бара на SignalBar + 1 равен 0, а цвет бара на SignalBar отличается от 0.
Вход в шорт: цвет бара на SignalBar + 1 равен 2, а цвет бара на SignalBar отличается от 2.
Выход из лонга: выполняется, если EnableLongExits = true и выполняется условие входа в шорт.
Выход из шорта: выполняется, если EnableShortExits = true и выполняется условие входа в лонг.
Защитные стопы и цели создаются через StartProtection с абсолютными смещениями цены, вычисляемыми как произведение StopLossPoints/TakeProfitPoints на PriceStep инструмента.
Параметры
Параметр
Описание
OrderVolume
Базовый объем заявки при открытии позиции.
CandleType
Таймфрейм для расчета индикаторов (по умолчанию 1 час).
FastPeriod / SlowPeriod
Длины скользящих средних внутри осциллятора Chaikin.
ChaikinMethod
Тип скользящей средней (SMA, EMA, SMMA, WMA) для линии накопления/распределения.
CciPeriod
Период индикатора CCI.
MomentumPeriod
Период индикатора Momentum.
SmoothingMethod
Метод сглаживания XMA. Значения JurX, Parabolic, T3 отображаются на Jurik MA; Vidya использует адаптивное сглаживание на базе CMO; Adaptive использует Kaufman AMA.
SmoothingLength
Количество баров для выбранного сглаживания.
SmoothingPhase
Дополнительный параметр для отдельных методов (например, период CMO для VIDYA или медленный период для AMA).
SignalBar
Смещение (в закрытых барах) при анализе смены цвета. Значение 1 соответствует настройке MetaTrader.
EnableLongEntries / EnableShortEntries
Разрешение на открытие длинных/коротких позиций.
EnableLongExits / EnableShortExits
Разрешение на закрытие позиций по сигналу индикатора.
StopLossPoints / TakeProfitPoints
Размеры стоп-лосса и тейк-профита в шагах цены (0 — без защиты).
Особенности реализации
Стратегия обрабатывает только завершенные свечи и использует Bind для потокового заполнения индикаторов.
Список методов сглаживания повторяет XMA из оригинальной библиотеки. Недоступные варианты заменены близкими аналогами (см. таблицу параметров).
Параметр MetaTrader VolumeType не используется, поскольку свечи StockSharp уже содержат нужные данные объема для линии накопления/распределения.
Денежный менеджмент оригинала строился на пользовательских функциях. В конверсии используется фиксированный объем OrderVolume.
Рекомендации по применению
Используйте инструменты с информативным объемом, чтобы Chaikin вел себя корректно. Для низколиквидных активов можно увеличить MomentumPeriod для фильтрации шума.
При оптимизации сглаживания сочетайте SmoothingLength и SmoothingPhase аккуратно: экстремальные комбинации сильно задерживают сигналы.
Значения по умолчанию (StopLossPoints = 1000, TakeProfitPoints = 2000) соответствуют крупным смещениям. Настройте их под шаг цены выбранного инструмента.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Strategy converted from the KWAN_CCC expert advisor.
/// Uses CCI and Momentum to detect trend transitions.
/// Enters long when CCI turns up while momentum is positive,
/// enters short when CCI turns down while momentum is negative.
/// </summary>
public class KwanCccStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private decimal _prevCci;
private decimal _prevClose;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
public KwanCccStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI length", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = 0m;
_prevClose = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = 0m;
_prevClose = 0m;
_initialized = false;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, (ICandleMessage candle, decimal cciValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_prevCci = cciValue;
_prevClose = candle.ClosePrice;
_initialized = true;
return;
}
var closeUp = candle.ClosePrice > _prevClose;
var closeDown = candle.ClosePrice < _prevClose;
// Buy when CCI crosses into positive territory and price confirms the move.
if (_prevCci <= 0m && cciValue > 0m && closeUp && Position <= 0)
{
BuyMarket();
}
// Sell when CCI crosses into negative territory and price confirms the move.
else if (_prevCci >= 0m && cciValue < 0m && closeDown && Position >= 0)
{
SellMarket();
}
_prevCci = cciValue;
_prevClose = candle.ClosePrice;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, cci);
DrawOwnTrades(area);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class kwan_ccc_strategy(Strategy):
def __init__(self):
super(kwan_ccc_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI length", "Indicators")
self._prev_cci = 0.0
self._prev_close = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def CciPeriod(self):
return self._cci_period.Value
def OnReseted(self):
super(kwan_ccc_strategy, self).OnReseted()
self._prev_cci = 0.0
self._prev_close = 0.0
self._initialized = False
def OnStarted2(self, time):
super(kwan_ccc_strategy, self).OnStarted2(time)
self._prev_cci = 0.0
self._prev_close = 0.0
self._initialized = False
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(cci, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def _on_process(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cv = float(cci_value)
close = float(candle.ClosePrice)
if not self._initialized:
self._prev_cci = cv
self._prev_close = close
self._initialized = True
return
close_up = close > self._prev_close
close_down = close < self._prev_close
if self._prev_cci <= 0 and cv > 0 and close_up and self.Position <= 0:
self.BuyMarket()
elif self._prev_cci >= 0 and cv < 0 and close_down and self.Position >= 0:
self.SellMarket()
self._prev_cci = cv
self._prev_close = close
def CreateClone(self):
return kwan_ccc_strategy()