Открыть на GitHub

Стратегия Proffessor v3

Обзор

Стратегия представляет собой полную конвертацию эксперта MetaTrader Proffessor v3 на высокоуровневый API StockSharp. Сохранена исходная идея: определять режим рынка по ADX и строить сетку защитных и усредняющих заявок вокруг текущей позиции.

  • Индикатор: Average Directional Index (ADX) с периодом 14 и линиями +DI/-DI.
  • Режимы: флэт (ADX ниже порога) и тренд (ADX выше порога).
  • Заявки: открывает рыночную позицию и выставляет симметричную сетку отложенных приказов для хеджирования, пирамидинга или среднения.
  • Выход: закрывает все позиции и отложенные заявки при достижении заданной прибыли или убытка.
  • Сеанс: торгует только в пределах выбранного диапазона часов.

Логика торговли

Определение режима

  1. Подписка на выбранный тип свечей и расчёт ADX.
  2. Задержка сигнала ADX на указанное количество закрытых свечей (BarOffset), аналогично CopyBuffer(handle, shift) в MQL.
  3. При отсутствии позиции анализируется последняя отложенная величина ADX:
    • Флэт, быки: ADX < AdxFlatLevel и +DI > -DI.
    • Флэт, медведи: ADX < AdxFlatLevel и +DI < -DI.
    • Тренд вверх: ADX ≥ AdxFlatLevel и +DI > -DI.
    • Тренд вниз: ADX ≥ AdxFlatLevel и +DI < -DI.

Расстановка заявок

После рыночного входа базовым объёмом создаётся симметричная сетка вокруг цены. Расстояния выражены в пунктах точь-в-точь как в MQL и автоматически масштабируются на размер шага цены инструмента.

  • Флэт, быки: длинная позиция, защитный sell-stop, buy limit ниже ask и sell limit выше bid для игры в коридоре.
  • Флэт, медведи: короткая позиция, защитный buy-stop, buy limit для перекрытия короткой позиции и sell limit выше для перевхода.
  • Тренд вверх: длинная позиция, sell-stop для хеджирования и buy-stop для пирамидинга по пробою.
  • Тренд вниз: короткая позиция, sell-stop по тренду и buy-stop против резкого разворота.

Шаг сетки рассчитывается по формуле оригинала: каждое новое колено добавляет GridStep + GridDeltaIncrement * level / 2. Объём ордеров корректируется через LotMultiplier и LotAddition, затем нормализуется по шагу и границам объёма инструмента.

Управление выходом

  • Нереализованная прибыль вычисляется по средней цене позиции и закрытию последней свечи.
  • При превышении ProfitTarget или падении ниже LossLimit (если он не ноль) стратегия закрывает позицию и отменяет все отложенные заявки.
  • Вне интервала [StartHour, EndHour) сделки не совершаются, как и в функции Time() оригинала.

Особенности реализации

  • Bid/ask для отложенных приказов приближаются как цена закрытия плюс/минус половина шага цены, что позволяет перенести тиковую логику на свечные данные.
  • Пункт (point) пересчитывается по шагу цены и корректируется для инструментов с тремя и пятью знаками после запятой, как в переменной m_adjusted_point.
  • Объёмы и цены нормализуются с учётом шагов, минимумов и максимумов биржи перед отправкой заявок.
  • Обработка ведётся только по завершённым свечам, чтобы исключить ложные сигналы.

Параметры

Параметр Описание
Volume Базовый объём рыночного ордера.
LotMultiplier Множитель для объёма отложенных заявок.
LotAddition Дополнительный объём после применения множителя.
MaxLevels Максимальное число уровней сетки на сторону.
GridDeltaIncrement Прибавка к шагу сетки для дальних уровней (в пунктах).
GridInitialOffset Расстояние до первой защитной заявки (в пунктах).
GridStep Базовый шаг между соседними уровнями (в пунктах).
ProfitTarget Уровень прибыли, при котором закрываются все позиции.
LossLimit Уровень убытка, инициирующий закрытие (0 — отключено).
AdxFlatLevel Порог ADX, разделяющий флэт и тренд.
BarOffset Число закрытых свечей, на которое сдвигается сигнал ADX.
StartHour Час начала торгового окна (UTC).
EndHour Час окончания торгового окна (UTC).
CandleType Тип свечей для расчётов.
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>
/// Proffessor V3 strategy. Uses RSI 50-line crossover (period 10).
/// </summary>
public class ProffessorV3Strategy : 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 ProffessorV3Strategy()
	{
		_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;
	}
}