Technical Trader — это перенос советника MetaTrader из MQL/22304/Technical_trader.mq5 на высокоуровневый API StockSharp. Стратегия объединяет два простых скользящих среднего с поиском ценовых кластеров ликвидности рядом с текущими котировками. Сделки открываются только тогда, когда найденный кластер подтверждает направление пересечения быстрых и медленных SMA, а управление рисками выполняется через стоп-лосс и тейк-профит, рассчитываемые в шагах цены, как и в оригинальном коде.
Общая информация
Платформа: высокоуровневый API StockSharp.
Данные: свечи выбранного таймфрейма и подписка на стакан для получения лучших цен bid/ask.
Тип стратегии: направленная торговля от кластеров ликвидности.
Связь с исходником: параметры SMA, выборка исторических закрытий, порог кластеризации и расчёт оффсетов полностью воспроизводят MQL-реализацию.
Логика торговли
Подписка на свечи заданного типа и расчёт двух SMA с периодами FastMaPeriod и SlowMaPeriod.
Поддержание скользящего окна (HistoryDepth) из последних закрытий с округлением до трёх знаков — аналог функции NormalizeDouble.
Построение гистограммы повторяемости цен и выделение уровней, у которых количество появлений превышает ResistanceThreshold.
Отслеживание актуальных bid/ask через подписку на стакан и использование цены закрытия свечи как резервного источника.
Условия входа в лонг:
Быстрая SMA выше медленной.
Найденный кластер расположен непосредственно под текущим ask и попадает в допуск LevelTolerance.
При отсутствии позиции либо при открытом шорте стратегия покупает объём, достаточный для переворота и открытия базового лонга.
Вход в шорт зеркален: кластер должен находиться над текущим bid, а быстрая SMA — ниже медленной.
После открытия позиции рассчитываются уровни стоп-лосса и тейк-профита как произведение шага цены (PriceStep) на StopLossPoints и TakeProfitPoints, что эквивалентно умножению на _Point в MQL.
На каждой закрывшейся свече стратегия проверяет, достигли ли bid/ask защитных уровней, и закрывает позицию при срабатывании стопа или цели.
Параметры
Параметр
Описание
Значение по умолчанию
FastMaPeriod
Период быстрой SMA для генерации сигнала.
25
SlowMaPeriod
Период медленной SMA для фильтра тренда.
30
StopLossPoints
Дистанция стопа в шагах цены (PriceStep * StopLossPoints).
30
TakeProfitPoints
Цель по прибыли в шагах цены (PriceStep * TakeProfitPoints).
100
ResistanceThreshold
Минимальное число повторений цены, чтобы считать её кластером ликвидности.
15
HistoryDepth
Количество свечей в окне анализа (для XAUUSD рекомендуется 100, как в исходнике).
500
LevelTolerance
Максимальное допустимое расстояние от текущего bid/ask до кластера.
0.0005
CandleType
Тип обрабатываемых свечей (таймфрейм или пользовательский тип).
Таймфрейм 1 минута
Особенности реализации
Подписка на стакан обеспечивает актуальные цены bid/ask и имитирует тиковое выполнение MQL-советника.
Подсчёт кластеров выполнен без LINQ и с повторным использованием буферов в соответствии с требованиями конверсии StockSharp.
Стоп-лосс и тейк-профит управляются внутри стратегии, поскольку в StockSharp ордера исполняются синтетически, а не брокером.
Визуализация добавляет на график свечи, обе SMA и сделки для удобства тестирования.
Рекомендации по настройке
Увеличивайте HistoryDepth на старших таймфреймах, чтобы сохранять релевантный объём данных для кластеризации.
Уменьшайте LevelTolerance на инструментах с мелким шагом цены для отсечения посторонних уровней.
Снижайте ResistanceThreshold на неликвидных рынках, где повторений меньше.
Размер позиции задаётся свойством Volume базового класса Strategy; скорректируйте его перед запуском стратегии.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class TechnicalTraderStrategy : 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 TechnicalTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 10).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 technical_trader_strategy(Strategy):
def __init__(self):
super(technical_trader_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", 10) \
.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(technical_trader_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(technical_trader_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 technical_trader_strategy()