Конвертация советника MetaTrader Exp_AverageChangeCandle в среду StockSharp. Алгоритм сглаживает отношения цен свечи к базовой скользящей средней, присваивает свечам цвета и торгует при смене цвета.
Идея
Рассчитать базовую скользящую среднюю (MaMethod1, Length1) по выбранной цене.
Выразить цену открытия и закрытия как отношение к базовой средней и возвести в степень Power.
Сгладить преобразованные значения второй средней (MaMethod2, Length2).
Определить цвет: бычий, если сглаженное закрытие выше открытия, медвежий — если ниже.
Отрабатывать смену цвета после задержки SignalBar.
Обрабатываются только завершённые свечи. Стратегия открывает позиции по направлению нового цвета и при необходимости закрывает противоположные.
Параметры
Параметр
Значение по умолчанию
Описание
OrderVolume
1
Объём сделки при открытии новой позиции.
MaMethod1
Lwma
Метод сглаживания базовой средней (поддержка SMA/EMA/SMMA/LWMA/JJMA/AMA, остальные переходят в EMA).
Length1
12
Период базовой средней.
Phase1
15
Параметр фазы для вариантов Jurik.
PriceSource
Median
Используемая цена перед вычислением базовой средней.
MaMethod2
Jjma
Метод сглаживания преобразованных отношений.
Length2
5
Период второй средней.
Phase2
100
Параметр фазы для второй средней.
Power
5
Степень для отношений открытия/закрытия.
SignalBar
1
Сколько закрытых баров ждать перед реакцией.
BuyOpenEnabled
true
Разрешить открытие длинных позиций.
SellOpenEnabled
true
Разрешить открытие коротких позиций.
BuyCloseEnabled
true
Закрывать лонги при сигнале на шорт.
SellCloseEnabled
true
Закрывать шорты при сигнале на лонг.
StopLossPoints
0
Абсолютный размер стоп-лосса, 0 отключает.
TakeProfitPoints
0
Абсолютный тейк-профит, 0 отключает.
CandleType
Таймфрейм H4
Тип свечей, которые обрабатываются стратегией.
Правила торговли
Бычья смена (color = 2): закрываем шорты (если разрешено) и открываем лонг при Position <= 0 и включённом BuyOpenEnabled.
Медвежья смена (color = 0): закрываем лонги (если разрешено) и открываем шорт при Position >= 0 и включённом SellOpenEnabled.
Цвет 1 (нейтральный) не вызывает сделок.
Для имитации оригинального кода сигнал оценивается по свече, отстоящей на SignalBar баров от последней завершённой.
Управление риском
StopLossPoints и TakeProfitPoints передаются в StartProtection как абсолютные расстояния. Нулевые значения отключают соответствующую защиту.
Примечания
Поддерживаются только те методы сглаживания, которые есть в StockSharp. JurX, ParMA, T3 и VIDYA заменены на EMA.
Параметры фаз сохранены ради совместимости и работают только для Jurik/Kaufman сглаживания.
Сделки исполняются рыночными ордерами, как в оригинальном советнике. Настройка проскальзывания из версии MQL не переносилась.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Average Change Candle strategy (simplified).
/// Compares smoothed open vs close ratios to detect bullish/bearish candle patterns.
/// Uses EMA of open and close to determine trend direction.
/// </summary>
public class AverageChangeCandleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal _prevSmoothedOpen;
private decimal _prevSmoothedClose;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public AverageChangeCandleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
_emaPeriod = Param(nameof(EmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA smoothing period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSmoothedOpen = 0m;
_prevSmoothedClose = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSmoothedOpen = 0;
_prevSmoothedClose = 0;
_initialized = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, (ICandleMessage candle, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
// Simple exponential smoothing of open and close
var alpha = 2m / (EmaPeriod + 1m);
if (!_initialized)
{
_prevSmoothedOpen = candle.OpenPrice;
_prevSmoothedClose = candle.ClosePrice;
_initialized = true;
return;
}
var smoothedOpen = alpha * candle.OpenPrice + (1m - alpha) * _prevSmoothedOpen;
var smoothedClose = alpha * candle.ClosePrice + (1m - alpha) * _prevSmoothedClose;
var prevBullish = _prevSmoothedClose > _prevSmoothedOpen;
var currBullish = smoothedClose > smoothedOpen;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevSmoothedOpen = smoothedOpen;
_prevSmoothedClose = smoothedClose;
return;
}
// Buy on transition to bullish smoothed candle
if (currBullish && !prevBullish && candle.ClosePrice > emaValue && Position <= 0)
{
BuyMarket();
}
// Sell on transition to bearish smoothed candle
else if (!currBullish && prevBullish && candle.ClosePrice < emaValue && Position >= 0)
{
SellMarket();
}
_prevSmoothedOpen = smoothedOpen;
_prevSmoothedClose = smoothedClose;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class average_change_candle_strategy(Strategy):
def __init__(self):
super(average_change_candle_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._ema_period = self.Param("EmaPeriod", 12) \
.SetDisplay("EMA Period", "EMA smoothing period", "Indicators")
self._prev_smoothed_open = 0.0
self._prev_smoothed_close = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaPeriod(self):
return self._ema_period.Value
def OnReseted(self):
super(average_change_candle_strategy, self).OnReseted()
self._prev_smoothed_open = 0.0
self._prev_smoothed_close = 0.0
self._initialized = False
def OnStarted2(self, time):
super(average_change_candle_strategy, self).OnStarted2(time)
self._prev_smoothed_open = 0.0
self._prev_smoothed_close = 0.0
self._initialized = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(ema, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
ev = float(ema_value)
o = float(candle.OpenPrice)
c = float(candle.ClosePrice)
alpha = 2.0 / (self.EmaPeriod + 1.0)
if not self._initialized:
self._prev_smoothed_open = o
self._prev_smoothed_close = c
self._initialized = True
return
smoothed_open = alpha * o + (1.0 - alpha) * self._prev_smoothed_open
smoothed_close = alpha * c + (1.0 - alpha) * self._prev_smoothed_close
prev_bullish = self._prev_smoothed_close > self._prev_smoothed_open
curr_bullish = smoothed_close > smoothed_open
if curr_bullish and not prev_bullish and c > ev and self.Position <= 0:
self.BuyMarket()
elif not curr_bullish and prev_bullish and c < ev and self.Position >= 0:
self.SellMarket()
self._prev_smoothed_open = smoothed_open
self._prev_smoothed_close = smoothed_close
def CreateClone(self):
return average_change_candle_strategy()