Стратегия переносит два советника из MetaTrader 5 — BrainTrend2_V2 и AbsolutelyNoLagLWMA. Каждый модуль подписывается на собственные свечи, самостоятельно определяет направление и формирует желаемый объем. В версии для StockSharp оба потока решений сохранены, а итоговая позиция рассчитывается как сумма целей двух модулей.
Модуль BrainTrend2. Использует цветовые состояния индикатора BrainTrend2. Индикатор строит ATR-подобный канал и меняет цвет, когда цена пробивает противоположную границу.
Модуль AbsolutelyNoLagLWMA. Рассчитывает двойное линейно-взвешенное среднее по выбранной цене и оценивает знак наклона полученной кривой.
Как только один из модулей хочет изменить направление, стратегия вычисляет новую суммарную цель и выставляет рыночные заявки, чтобы привести фактическую позицию к этому значению. По умолчанию оба индикатора работают с четырехчасовыми свечами, но для каждого модуля можно выбрать собственный таймфрейм.
Индикаторы
BrainTrend2
Индикатор заново реализован в C#, чтобы повторить пятицветную раскраску:
Вычисляется треугольная скользящая сумма истинного диапазона с заданным периодом, умноженная на коэффициент 0.7 (widcha).
Плавающее значение Emaxtra следует за экстремумами внутри текущего режима.
При пробое low < Emaxtra - widcha режим меняется на медвежий, при high > Emaxtra + widcha — на бычий.
Значения 0 и 1 означают бычий цвет, 3 и 4 — медвежий, 2 — индикатор еще не готов.
AbsolutelyNoLagLWMA
Модуль применяет два последовательных линейно-взвешенных скользящих средних к выбранной ценовой серии:
2 (синий) — линия растет.
1 (серый) — линия горизонтальна.
0 (фиолетовый) — линия падает.
Оба индикатора выставляют IsFormed, поэтому стратегия начинает реагировать только когда собрана достаточная история.
Логика торговли
Внутри стратегии поддерживаются переменные _brainTrendTarget и _lwmaTarget, задающие желаемый объем для каждого модуля. После изменения любой из целей вызывается RebalancePosition, который доводит фактическую позицию до суммы этих значений.
Модуль BrainTrend2
Анализирует цвет свечи на расстоянии SignalBar (по умолчанию одна свеча) и цвет перед ней.
Если текущий цвет бычий (< 2), а предыдущий не был бычьим (> 1):
Закрываются короткие позиции, открытые этим модулем.
При разрешенных входах открывается лонг объемом BrainTrendVolume.
Если текущий цвет медвежий (> 2), а предыдущий не был медвежьим (< 3):
Закрываются лонги модуля.
При разрешенных входах открывается шорт объемом BrainTrendVolume.
Модуль AbsolutelyNoLagLWMA
Реагирует только на смену цветов 2 (вверх) и 0 (вниз) с тем же смещением SignalBar.
При появлении цвета 2 и изменении цвета относительно предыдущей свечи:
При LwmaCloseShortAllowed = true закрывается шорт этого модуля.
При LwmaBuyAllowed = true открывается лонг объемом LwmaVolume.
При появлении цвета 0 и изменении цвета:
При LwmaCloseLongAllowed = true закрывается лонг модуля.
При LwmaSellAllowed = true открывается шорт объемом LwmaVolume.
Модули воздействуют только на собственные целевые объемы, поэтому могут работать одновременно. Например, BrainTrend2 удерживает трендовую позицию, а LWMA временно хеджирует ее короткими сделками.
Параметры
Параметр
Описание
BrainTrendAtrPeriod
Период треугольного ATR для BrainTrend2.
BrainTrendSignalBar
Сколько завершенных свечей ждать перед сигналом BrainTrend2. Значение 1 означает работу по предыдущей свече.
BrainTrendBuyAllowed / BrainTrendSellAllowed
Разрешение на лонги/шорты для модуля BrainTrend2.
BrainTrendVolume
Объем позиции BrainTrend2 при входе.
BrainTrendCandleType
Таймфрейм свечей, которые использует BrainTrend2.
LwmaLength
Длина линейно-взвешенного среднего в индикаторе AbsolutelyNoLagLWMA.
LwmaSignalBar
Смещение сигналов LWMA.
LwmaAppliedPrice
Тип цены для LWMA (close, open, median, Demark и т.д.).
LwmaBuyAllowed / LwmaSellAllowed
Разрешение на лонги/шорты для LWMA.
LwmaCloseLongAllowed / LwmaCloseShortAllowed
Разрешение закрывать противоположные позиции при смене цвета.
LwmaVolume
Объем сделок LWMA.
LwmaCandleType
Таймфрейм свечей для LWMA.
Управление позицией
Всегда используются рыночные заявки BuyMarket / SellMarket для достижения целевого объема.
Объемы модулей суммируются. Если один модуль лонгует, а второй шортит на тот же объем, чистая позиция будет равна нулю.
Брокерские функции установки стоп-лоссов и тейк-профитов из оригинального советника не переносились. При необходимости можно подключить защитные механизмы StockSharp.
При работе на разных таймфреймах стратегия автоматически подписывается на обе серии свечей и отображает их на графике вместе со сделками.
Примечания
Расчеты индикаторов полностью реализованы внутри стратегии и не требуют внешних библиотек.
Значение SignalBar = 0 позволяет реагировать сразу после закрытия текущей свечи, большие значения добавляют подтверждение.
BrainTrend2 требует минимум AtrPeriod + 2 исторических свечей, AbsolutelyNoLagLWMA — не меньше Length.
Оба модуля работают с одним инструментом Strategy.Security, аналогично тому, как в MT5 позиции разделялись по различным magic number.
Рекомендации по доработке
При необходимости добавьте риск-менеджер или трейлинг-стопы из инфраструктуры StockSharp.
Настройте BrainTrendVolume и LwmaVolume отдельно, чтобы сместить баланс между трендовой и контртрендовой частью.
Используйте значения индикаторов в методах ProcessBrainTrend и ProcessLwma, чтобы ввести дополнительные фильтры или подтверждения.
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>
/// BrainTrend2 + AbsolutelyNoLagLwma strategy (simplified). Uses ATR-based
/// trend detection combined with weighted MA direction for entries.
/// Implemented as EMA crossover with ATR channel filter.
/// </summary>
public class BrainTrend2AbsolutelyNoLagLwmaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaFastLength;
private readonly StrategyParam<int> _emaSlowLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrCoefficient;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaFastLength
{
get => _emaFastLength.Value;
set => _emaFastLength.Value = value;
}
public int EmaSlowLength
{
get => _emaSlowLength.Value;
set => _emaSlowLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal AtrCoefficient
{
get => _atrCoefficient.Value;
set => _atrCoefficient.Value = value;
}
public BrainTrend2AbsolutelyNoLagLwmaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_emaFastLength = Param(nameof(EmaFastLength), 7)
.SetGreaterThanZero()
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators");
_emaSlowLength = Param(nameof(EmaSlowLength), 21)
.SetGreaterThanZero()
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Indicators");
_atrCoefficient = Param(nameof(AtrCoefficient), 0.7m)
.SetGreaterThanZero()
.SetDisplay("ATR Coeff", "ATR multiplier for channel", "Logic");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFast = new ExponentialMovingAverage { Length = EmaFastLength };
var emaSlow = new ExponentialMovingAverage { Length = EmaSlowLength };
decimal prevFast = 0, prevSlow = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaFast, emaSlow, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastVal;
prevSlow = slowVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastVal;
prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
// Fast EMA crosses above slow - bullish
var bullishCross = prevFast <= prevSlow && fastVal > slowVal;
// Fast EMA crosses below slow - bearish
var bearishCross = prevFast >= prevSlow && fastVal < slowVal;
if (bullishCross && Position <= 0)
BuyMarket();
else if (bearishCross && Position >= 0)
SellMarket();
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaFast);
DrawIndicator(area, emaSlow);
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 brain_trend2_absolutely_no_lag_lwma_strategy(Strategy):
def __init__(self):
super(brain_trend2_absolutely_no_lag_lwma_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ema_fast_length = self.Param("EmaFastLength", 7) \
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators")
self._ema_slow_length = self.Param("EmaSlowLength", 21) \
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaFastLength(self):
return self._ema_fast_length.Value
@property
def EmaSlowLength(self):
return self._ema_slow_length.Value
def OnReseted(self):
super(brain_trend2_absolutely_no_lag_lwma_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(brain_trend2_absolutely_no_lag_lwma_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self.EmaFastLength
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self.EmaSlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema_fast, ema_slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_fast)
self.DrawIndicator(area, ema_slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return brain_trend2_absolutely_no_lag_lwma_strategy()