Стратегия переносит советник MetaTrader Trickerless RHMP на высокоуровневый API StockSharp. Сохранена многоступенчатая логика
входа из оригинала: подтверждение тренда индикатором ADX, фильтрация направления сглаженными скользящими средними и управление
рискoм через ATR. Реализация полностью соответствует требованиям AGENTS.md.
Логика торговли
Используемые индикаторы
Average True Range (ATR) с настраиваемым периодом для расчёта защитных расстояний.
Average Directional Index (ADX) с компонентами +DI/-DI для оценки силы тренда.
Две сглаженные скользящие средние (SMMA), выступающие быстрым и медленным фильтрами.
Оценка тренда
Наклон медленной SMMA должен находиться внутри коридора MinSlopePips…MaxSlopePips (в пунктах инструмента).
Значение ADX обязано превышать AdxThreshold и расти относительно предыдущей свечи.
Цена должна располагаться минимум на TrendSpacePips пунктов от быстрой SMMA, чтобы избегать флетовых фаз.
Для длинного приоритета быстрая SMMA выше медленной, +DI ≥ -DI и быстрая средняя растёт. Условия для короткого приоритета
зеркальные.
Основные входы
При активном бычьем/медвежьем сигнале открывается позиция объёмом OrderVolume. Учитавается ограничение MaxNetPositions
и выдерживается пауза SleepInterval между входами.
Если открыта противоположная позиция, она закрывается перед сменой направления (хеджирование отключено).
Дополнительные входы по «шипам»
Если диапазон текущей свечи превышает диапазон предыдущей более чем в CandleSpikeMultiplier раз и направление подтверждено
компонентами ADX, стратегия может добавить позицию объёмом OrderVolume * SpikeVolumeMultiplier по направлению тела свечи.
Управление риском
Стоп-лосс, тейк-профит и, при необходимости, трейлинг-стоп рассчитываются через ATR (StopLossAtrMultiplier,
TakeProfitAtrMultiplier, TrailingAtrMultiplier).
Сессионный контроль: после достижения DailyProfitTarget (доля стартового капитала) новые сделки блокируются.
Флаг EmergencyExit мгновенно закрывает все позиции при срабатывании.
Параметры
Параметр
Описание
Значение по умолчанию
CandleType
Таймфрейм анализа.
5-минутные свечи
OrderVolume
Базовый объём сделки.
0.03
AtrPeriod
Период ATR.
14
AdxPeriod
Период ADX.
14
AdxThreshold
Минимальное значение ADX для торговли.
10
FastMaPeriod
Период быстрой SMMA.
60
SlowMaPeriod
Период медленной SMMA.
120
MinSlopePips / MaxSlopePips
Допустимый наклон медленной SMMA.
2 / 9
TrendSpacePips
Минимальная дистанция цены от быстрой SMMA (в пунктах).
5
CandleSpikeMultiplier
Множитель диапазона для активации «шиповых» входов.
7
TakeProfitAtrMultiplier
ATR-множитель тейк-профита.
1.0
StopLossAtrMultiplier
ATR-множитель стоп-лосса.
1.5
TrailingAtrMultiplier
ATR-множитель трейлинг-стопа (0 — отключено).
0
MaxNetPositions
Максимальное число нетто-позиций.
1
SleepInterval
Минимальная пауза между входами.
24 минуты
DailyProfitTarget
Доля стартового капитала, после которой торговля останавливается.
0.045
AllowNewEntries
Разрешение на открытие новых позиций.
true
SpikeVolumeMultiplier
Коэффициент объёма для «шиповых» сделок.
1.0
EmergencyExit
При true закрывает все позиции и завершает торговлю.
false
Примечания
Версия для StockSharp использует высокоуровневый API и не повторяет построчное управление ордерами MetaTrader. Масштабирование
и защита выполняются через базовый объём и ATR.
Проверки баланса и маржи из оригинального советника заменены комбинацией DailyProfitTarget, MaxNetPositions и ATR-защит,
что сохраняет идеологию управления риском без прямых MT4-вызовов.
Сглаженные скользящие требуют тёплого периода: перед оценкой результатов убедитесь, что в тесте/онлайне достаточно исторических
данных.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Trickerless RHMP" MetaTrader expert.
/// Uses fast/slow EMA crossover for entries.
/// Fast EMA above slow = buy, below = sell.
/// </summary>
public class TrickerlessRhmpStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private ExponentialMovingAverage _fastMa;
private ExponentialMovingAverage _slowMa;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
public TrickerlessRhmpStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
_fastMaPeriod = Param(nameof(FastMaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast MA", "Fast EMA period", "Indicators");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow MA", "Slow SMA period (computed manually)", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var crossUp = _prevFast.Value <= _prevSlow.Value && fastValue > slowValue;
var crossDown = _prevFast.Value >= _prevSlow.Value && fastValue < slowValue;
if (crossUp)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_fastMa = null;
_slowMa = null;
_prevFast = null;
_prevSlow = null;
base.OnReseted();
}
}