Стратегия Six Indicators Momentum
Стратегия переносит советник MetaTrader 4 6xIndics_M на высокоуровневый API StockSharp. Она объединяет шесть источников импульса, полученных из индикаторов Билла Вильямса Accelerator Oscillator (AC) и Awesome Oscillator (AO), пропускает их через настраиваемую матрицу условий и фильтрует результат медленным стохастиком. В любой момент времени открыта только одна позиция; реализованы мартингейл, фиксированные стоп/тейк и опциональный трейлинг-стоп — полностью как в оригинальном советнике.
Алгоритм работы
- Подписка на данные — используется серия свечей
CandleType (по умолчанию часовые свечи).
- Индикаторы:
- Awesome Oscillator вычисляет разницу между 5- и 34-периодной SMA от медианной цены.
- Скользящая SMA с периодом 5 от AO формирует значения Accelerator Oscillator.
- Стохастик с параметрами 5/5/5 предоставляет линию %K, взятую с предыдущей закрытой свечи (сдвиг 1 в MT4).
- Шесть слотів индикаторов — на каждой завершённой свече обновляются буферы:
- Слот 0:
AC[1] — значение AC со сдвигом 1.
- Слот 1:
AC[10] — значение AC со сдвигом 10.
- Слот 2:
AC[20] — значение AC со сдвигом 20.
- Слот 3: импульс AO
AO[0] - AO[shift], где shift задаётся параметром AoMomentumShift.
- Слот 4: импульс AC
AC[0] - AC[shift #1] (AcPrimaryShift).
- Слот 5: импульс AC
AC[0] - AC[shift #2] (AcSecondaryShift).
- Матрица условий — параметры
FirstSourceIndex … SixthSourceIndex выбирают, какой слот задействовать в каждом из шести проверок (аналог переменных k/u/t/e/r/o в MT4). Те же значения используются при досрочном закрытии позиции через CloseOnReverseSignal.
- Правила входа:
- Покупка:
A > 0, B > 0.0001 × Sensitivity, C > 0.0002 × Sensitivity, D < 0, E < 0.0001 × Sensitivity, F < 0.0002 × Sensitivity, стохастик %K (предыдущая свеча) < 15.
- Продажа:
A < 0, B < 0.0001 × Sensitivity, C < 0.0002 × Sensitivity, D > 0, E > 0.0001 × Sensitivity, F > 0.0002 × Sensitivity, стохастик %K > 85.
- Сопровождение позиции:
- Одновременно допускается только одна позиция — при открытой сделке новые входы игнорируются.
- Стоп-лосс и тейк-профит вводятся в «пунктах» MT4 и переводятся в цену с учётом шага котировки (
Point).
- Трейлинг-стоп активируется после прохождения
TrailingStopPips (и, при включённом RequireProfitForTrailing, дополнительного LockProfitPips) и сдвигает стоп только в прибыльную сторону.
CloseOnReverseSignal закрывает прибыльную позицию при появлении встречного сигнала (Bid выше цены входа для лонга, Ask ниже цены входа для шорта).
- Мартингейл — если предыдущая сделка закрылась с убытком или в ноль, объём следующей сделки умножается на
(TakeProfitPips + StopLossPips) / TakeProfitPips. При прибыльном закрытии объём возвращается к базовому Volume.
Параметры
| Параметр |
Описание |
По умолчанию |
AllowBuy, AllowSell |
Разрешить сделки в длинную/короткую сторону. |
true |
CloseOnReverseSignal |
Закрывать прибыльную позицию при встречном сигнале. |
false |
FirstSourceIndex … SixthSourceIndex |
Номера слотов (0–5), участвующих в шести проверках. Значения вне диапазона усекаются. |
1,2,3,4,3,4 |
AoMomentumShift |
Сдвиг AO для расчёта слота 3. |
10 |
AcPrimaryShift, AcSecondaryShift |
Сдвиги AC для расчёта слотов 4 и 5. |
10 / 10 |
SensitivityMultiplier |
Множитель для порогов 0.0001 и 0.0002. |
1.0 |
TakeProfitPips, StopLossPips |
Расстояние до тейка/стопа в пунктах MT4. |
300 / 300 |
UseTrailingStop |
Включить трейлинг-стоп. |
false |
TrailingStopPips |
Расстояние трейлинг-стопа (в пунктах). |
300 |
RequireProfitForTrailing |
Требовать дополнительную прибыль LockProfitPips перед запуском трейлинга. |
false |
LockProfitPips |
Дополнительная прибыль в пунктах перед запуском трейлинга. |
300 |
Volume |
Базовый объём сделки. |
0.1 |
UseMartingale |
Включить мартингейл после убытков. |
false |
CandleType |
Тип свечей, используемых в расчётах. |
TimeSpan.FromHours(1) |
Рекомендации
- Обработка выполняется только по закрытию свечи, как и в MT4-версии с проверкой
prevtime.
- Хранится лишь необходимая история (до 256 баров), что позволяет воспроизводить обращения со сдвигом без вызова
GetValue().
- Триггеры стопа и трейлинга моделируются по максимуму/минимуму свечи; в реальной торговле используйте реальные стоп-заявки.
- Расчёт объёма учитывает
VolumeStep, MinVolume и MaxVolume, чтобы удовлетворить биржевым ограничениям.
- Отключение
AllowBuy или AllowSell блокирует новые входы, но встречный сигнал всё равно можно использовать для раннего закрытия (CloseOnReverseSignal).
Отличия от оригинального советника MT4
- Используются готовые индикаторы StockSharp (AO и SMA), исключая ручную работу с буферами.
- Входы выполняются через
BuyMarket/SellMarket, выходы через ClosePosition(), тогда как MT4-версия вызывала OrderSend/OrderClose.
- Объём приводится к шагу и ограничивается
MinVolume/MaxVolume инструмента.
- Добавлены визуальные помощники (
DrawCandles, DrawIndicator, DrawOwnTrades) для проверки стратегии на графике.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Six Indicators Momentum: RSI + EMA trend with ATR stops.
/// </summary>
public class SixIndicatorsMomentumStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public SixIndicatorsMomentumStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 30)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0; _entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevRsi = rsiVal;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class six_indicators_momentum_strategy(Strategy):
def __init__(self):
super(six_indicators_momentum_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI Length", "RSI period", "Indicators")
self._ema_length = self.Param("EmaLength", 30).SetDisplay("EMA Length", "Trend filter", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(six_indicators_momentum_strategy, self).OnReseted()
self._prev_rsi = 0
self._entry_price = 0
def OnStarted2(self, time):
super(six_indicators_momentum_strategy, self).OnStarted2(time)
self._prev_rsi = 0
self._entry_price = 0
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(rsi, ema, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._prev_rsi == 0 or atr_val <= 0:
self._prev_rsi = rsi_val
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + atr_val * 2.5 or close <= self._entry_price - atr_val * 1.5 or rsi_val > 70:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if close <= self._entry_price - atr_val * 2.5 or close >= self._entry_price + atr_val * 1.5 or rsi_val < 30:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if rsi_val > 55 and self._prev_rsi <= 55 and close > ema_val:
self._entry_price = close
self.BuyMarket()
elif rsi_val < 45 and self._prev_rsi >= 45 and close < ema_val:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rsi_val
def CreateClone(self):
return six_indicators_momentum_strategy()