Платформа: StockSharp (C#), высокоуровневый API стратегий
Идея: поиск разворотных сигналов по изменению наклона объёмно-взвешенного RSI-гистограммы
Инструменты: один тикер, один таймфрейм (по умолчанию H4)
Реализация повторяет механику буферов цвета из оригинального эксперта MT5. Мы вычисляем RSI, умножаем его на выбранный объём, сглаживаем полученную серию и отслеживаем моменты, когда наклон изменяется с роста на падение либо наоборот. В зависимости от разрешений открываются/закрываются длинные и короткие позиции.
Схема расчёта
RSI с периодом RsiPeriod вычисляется по подписанным свечам и смещается на −50, чтобы колебаться вокруг нуля.
Источник объёма выбирается параметром Use Tick Volume: при значении true используется тиковый объём, иначе — фактический объём свечи.
Гистограмма — произведение центрированного RSI и выбранного объёма, что усиливает движения при активной торговле.
Сглаживание — к гистограмме и объёму применяется одинаковое скользящее среднее (SmoothLength, тип SMA/EMA/SMMA/WMA). Работа продолжается только после формирования обоих сглаженных значений.
Цвет наклона определяется сравнением текущего и предыдущего сглаженных значений: рост → цвет 0, падение → цвет 1, равенство → сохранение предыдущего цвета.
Параметры
Параметр
Значение по умолчанию
Назначение
Candle Type
H4
Таймфрейм свечей для подписки.
RSI Period
14
Период RSI.
Smoothing Length
12
Длина скользящего среднего для RSI×Volume и объёма.
Smoothing Method
SMA
Тип скользящего среднего (SMA, EMA, SMMA, WMA).
Use Tick Volume
true
Использовать тиковый объём (true) или объём сделок (false).
Allow Buy Open
true
Разрешить открытие длинных позиций.
Allow Sell Open
true
Разрешить открытие коротких позиций.
Allow Buy Close
true
Разрешить закрытие длинных позиций при противоположном сигнале.
Allow Sell Close
true
Разрешить закрытие коротких позиций при противоположном сигнале.
Важно: экзотические методы сглаживания (JJMA, VIDYA, AMA и др.) отсутствуют в StockSharp, поэтому доступны только стандартные MA.
Правила торговли
Дождаться формирования обоих сглаживающих индикаторов.
Считать цвета (наклон) двух последних завершённых свечей.
Если предпоследний цвет = 0 (рост):
Закрыть короткую позицию (если разрешено).
Если последний цвет = 1 и разрешены покупки, открыть длинную позицию.
Если предпоследний цвет = 1 (падение):
Закрыть длинную позицию (если разрешено).
Если последний цвет = 0 и разрешены продажи, открыть короткую позицию.
При каждом сигнале стратегия действует на закрытии новой свечи, не дожидаясь дополнительных подтверждений.
Рекомендации по применению
Подберите таймфрейм под инструмент: на более низких периодах сигналов больше, но и шум выше.
Для ограничения риска подключите защиту через StartProtection (стоп-лосс/тейк-профит).
Для отладки полезно сопоставить наклон гистограммы в StockSharp и в оригинальном индикаторе MT5.
Отличия от MQL5 версии
Отказались от функций money-management из TradeAlgorithms.mqh, объём заявок задаётся параметрами стратегии.
Поддерживаются лишь те виды MA, которые есть в StockSharp; прочие варианты сведены к поведению SMA.
Нет отдельной задержки SignalBar/TimeShiftSec — сделки исполняются сразу после закрытия свечи.
Стопы/цели не зашиты жёстко, их добавляет пользователь при необходимости.
Ограничения
Необходимо наличие данных по объёму или тикам; без них амплитуда сигнала не совпадёт с оригиналом.
Стратегия не рисует саму гистограмму, фокусируется на торговых решениях и опциональной визуализации RSI.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// XRSI Histogram Vol Direct strategy. Uses RSI 50-line crossover.
/// </summary>
public class XrsiHistogramVolDirectStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private decimal? _prevRsi;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public XrsiHistogramVolDirectStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevRsi = rsiVal; return; }
if (_prevRsi == null) { _prevRsi = rsiVal; return; }
if (_prevRsi.Value < 45m && rsiVal >= 55m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (_prevRsi.Value > 55m && rsiVal <= 45m && Position >= 0) { if (Position > 0) SellMarket(); 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
from StockSharp.Algo.Strategies import Strategy
class xrsi_histogram_vol_direct_strategy(Strategy):
def __init__(self):
super(xrsi_histogram_vol_direct_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
def OnReseted(self):
super(xrsi_histogram_vol_direct_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(xrsi_histogram_vol_direct_strategy, self).OnStarted2(time)
self._prev_rsi = None
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self._prev_rsi is None:
self._prev_rsi = rv
return
if self._prev_rsi < 45.0 and rv >= 55.0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_rsi > 55.0 and rv <= 45.0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return xrsi_histogram_vol_direct_strategy()