No Nonsense Tester – порт эксперта MQL4 "NoNonsenseTester" на платформу StockSharp. Реализация повторяет ключевую идеологию NNFX: сначала определяется трендовая базовая линия, затем проверяются два подтверждающих индикатора, дополнительно оценивается волатильность через ATR, после чего сделки сопровождаются строгими правилами выхода. Все важные параметры вынесены в StrategyParam, что позволяет проводить многофакторную оптимизацию в экосистеме StockSharp.
Логика торговли
Базовая линия – экспоненциальная скользящая средняя с настраиваемым периодом задаёт направление тренда. Сделки рассматриваются только при закрытии цены по другую сторону линии.
Первое подтверждение – RSI должен находиться выше заданного уровня для покупок либо ниже взаимодополняющего уровня для продаж.
Второе подтверждение – CCI обязан совпадать с направлением сигнала и превышать заданную абсолютную величину, отсекая слабые импульсы.
Фильтр волатильности – значение ATR должно быть не ниже AtrMinimum, что защищает стратегию от торговли во флэтовых условиях.
Вход – при выполнении всех условий стратегия открывает позицию по направлению пробоя. Объём может масштабироваться с учётом ATR через параметр AtrEntryMultiplier.
Стоп и цель – сразу после входа рассчитываются уровни стоп-лосса и тейк-профита в кратных ATR. При необходимости включается трейлинг-стоп, который подтягивает защиту вслед за движением.
Выходной индикатор – дополнительный RSI меньшего периода контролирует открытые позиции и может досрочно закрыть их при выходе за границы допустимых зон.
Параметры
Параметр
Описание
BaselineLength
Период EMA, используемой в качестве базовой линии.
ConfirmationRsiLength
Период подтверждающего RSI.
ConfirmationRsiThreshold
Уровень RSI, разделяющий бычьи и медвежьи сигналы.
ConfirmationCciLength
Период подтверждающего CCI.
ConfirmationCciThreshold
Минимальное абсолютное значение CCI для приёма сигнала.
AtrPeriod
Период расчёта ATR.
AtrEntryMultiplier
Множитель ATR для масштабирования объёма.
AtrTakeProfitMultiplier
Множитель ATR для тейк-профита.
AtrStopLossMultiplier
Множитель ATR для стоп-лосса.
AtrTrailingMultiplier
Множитель ATR для трейлинг-стопа (0 отключает).
AtrMinimum
Минимально допустимое значение ATR перед входом.
ExitRsiLength
Период выходного RSI.
ExitRsiUpperLevel
Верхняя граница RSI, принудительно закрывающая продажи.
ExitRsiLowerLevel
Нижняя граница RSI, принудительно закрывающая покупки.
CandleType
Тип свечей (таймфрейм), используемых в расчётах.
Объекты графика
Стратегия автоматически выводит:
базовые свечи;
EMA-базовую линию;
отметки совершённых сделок.
Рекомендации по оптимизации
Каждый параметр StrategyParam снабжён диапазоном оптимизации, что позволяет повторить сеточные тесты оригинального советника. Используйте инструменты оптимизации StockSharp для подбора периодов, порогов и риск-параметров.
Советы по использованию
Настройте пороги под свои наборы индикаторов NNFX, чтобы быстро валидировать собственные идеи.
Следите за AtrMinimum, чтобы стратегия не торговала в периоды низкой волатильности.
Для тестирования продолжений тренда задайте AtrTrailingMultiplier больше нуля – это позволит фиксировать прибыль и одновременно оставлять пространство движению.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// No Nonsense Tester strategy: EMA baseline + RSI confirmation + CCI confirmation.
/// Buys when price above EMA, RSI above 50, CCI above 0.
/// Sells when price below EMA, RSI below 50, CCI below 0.
/// </summary>
public class NoNonsenseTesterStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _cciPeriod;
private bool _wasBullish;
private bool _hasPrevSignal;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public NoNonsenseTesterStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 25)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA baseline period", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI confirmation period", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI confirmation period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wasBullish = false;
_hasPrevSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrevSignal = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, rsi, cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal rsi, decimal cci)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var isBullish = close > ema && rsi > 50 && cci > 0;
if (_hasPrevSignal && isBullish != _wasBullish)
{
if (isBullish && Position <= 0)
BuyMarket();
else if (!isBullish && close < ema && rsi < 50 && cci < 0 && Position >= 0)
SellMarket();
}
_wasBullish = isBullish;
_hasPrevSignal = true;
}
}
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, RelativeStrengthIndex, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class no_nonsense_tester_strategy(Strategy):
def __init__(self):
super(no_nonsense_tester_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 25) \
.SetDisplay("EMA Period", "EMA baseline period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI confirmation period", "Indicators")
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI confirmation period", "Indicators")
self._ema = None
self._rsi = None
self._cci = None
self._was_bullish = False
self._has_prev_signal = False
@property
def ema_period(self):
return self._ema_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def cci_period(self):
return self._cci_period.Value
def OnReseted(self):
super(no_nonsense_tester_strategy, self).OnReseted()
self._ema = None
self._rsi = None
self._cci = None
self._was_bullish = False
self._has_prev_signal = False
def OnStarted2(self, time):
super(no_nonsense_tester_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
self._cci = CommodityChannelIndex()
self._cci.Length = self.cci_period
self._has_prev_signal = False
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(15)))
subscription.Bind(self._ema, self._rsi, self._cci, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value, rsi_value, cci_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._rsi.IsFormed or not self._cci.IsFormed:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
rsi_val = float(rsi_value)
cci_val = float(cci_value)
is_bullish = close > ema_val and rsi_val > 50.0 and cci_val > 0.0
if self._has_prev_signal and is_bullish != self._was_bullish:
if is_bullish and self.Position <= 0:
self.BuyMarket()
elif not is_bullish and close < ema_val and rsi_val < 50.0 and cci_val < 0.0 and self.Position >= 0:
self.SellMarket()
self._was_bullish = is_bullish
self._has_prev_signal = True
def CreateClone(self):
return no_nonsense_tester_strategy()