Открыть на GitHub

Стратегия Parallax Sell

Обзор

Parallax Sell — это конвертированная в StockSharp короткая маржинальная стратегия из советника MetaTrader parallax_sell. Исходный робот торговал кроссы с JPY (CAD/JPY и CHF/JPY) и использовал сочетание индикаторов Williams %R, MACD и стохастика для поиска перекупленных участков рынка. Выходы строятся на признаках ослабления импульса по Williams %R или медленному стохастику, а схема управления объёмом с элементами мартингейла увеличивает позицию после убыточных серий.

Логика входа

  • Работает на настраиваемом таймфрейме (по умолчанию свечи 1 час).
  • Ожидает закрытия новой свечи.
  • Требует, чтобы Williams %R с периодом 350 поднялся выше порога перекупленности (по умолчанию -10).
  • Требует, чтобы основная линия MACD (12/120/9) находилась выше заданного порога (по умолчанию 0.178), что подтверждает восходящий импульс перед продажей.
  • Отслеживает пересечение сверху вниз быстрой линии стохастика %K (период 10, замедление 3) ниже входного уровня (по умолчанию 90). Только такое пересечение инициирует новую короткую позицию.
  • Каждый сигнал открывает дополнительный рыночный ордер на продажу, что позволяет накапливать несколько коротких сделок в соответствии с логикой мартингейла.

Логика выхода

  • Рассчитывает плавающую прибыль всех открытых шортов в пунктах относительно размера пункта инструмента.
  • Если открыт только один шорт и средняя прибыль превышает целевой уровень одиночной сделки (по умолчанию 10 пунктов) и Williams %R падает ниже порога выхода (по умолчанию -80), позиция закрывается.
  • Если открыто более одной короткой позиции и средняя прибыль корзины превышает целевой уровень (по умолчанию 15 пунктов) и медленный стохастик %K (период 90, замедление 1) падает ниже триггера перепроданности (по умолчанию 12), закрывается вся корзина.
  • Дополнительно используется страховочный тейк-профит: корзина закрывается, когда средняя прибыль достигает заданного расстояния (по умолчанию 100 пунктов).

Управление объёмом

  • Стартовый объём — базовый лот (по умолчанию 0.01).
  • После прибыльного цикла (рост реализованной прибыли) следующий ордер возвращается к базовому объёму.
  • После убыточного цикла (снижение реализованной прибыли) объём следующего ордера умножается на коэффициент мартингейла (по умолчанию 1.6). Значения автоматически приводятся к шагу объёма инструмента.

Риск-менеджмент

  • Регистрируется защитный тейк-профит на заданном расстоянии в пунктах. Жёсткий стоп-лосс не используется — выходы управляются индикаторами.
  • Защита (StartProtection) запускается один раз, в соответствии с требованиями к конвертации.

Параметры

Имя Описание Значение по умолчанию
CandleType Таймфрейм расчётов. Свечи 1 часа
EntryWilliamsLength Период Williams %R для входов. 350
ExitWilliamsLength Период Williams %R для выходов. 350
EntryStochasticLength / Signal / Slowing Параметры быстрого стохастика для входа. 10 / 1 / 3
ExitStochasticLength / Signal / Slowing Параметры медленного стохастика для выхода. 90 / 7 / 1
MacdFastLength / MacdSlowLength / MacdSignalLength Настройки MACD. 12 / 120 / 9
EntryWilliamsThreshold Минимальное значение Williams %R перед открытием шорта. -10
ExitWilliamsThreshold Значение Williams %R, подтверждающее выход из одиночного шорта. -80
EntryStochasticTrigger Уровень, ниже которого должна упасть %K для входа. 90
ExitStochasticTrigger Уровень, ниже которого должен опуститься медленный стохастик для выхода. 12
MacdThreshold Минимальное значение основной линии MACD. 0.178
SingleTradeTargetPips Цель по прибыли (пункты) для одной позиции. 10
MultiTradeTargetPips Цель по прибыли (пункты) для корзины. 15
TakeProfitPips Жёсткий тейк-профит (пункты). 100
InitialVolume Базовый объём сделки. 0.01
MartingaleMultiplier Множитель объёма после убытка. 1.6
UseMartingale Включение логики мартингейла. true

Примечания

  • Стратегия работает только в короткую сторону и предполагает форексную трактовку пунктов.
  • Средняя прибыль вычисляется как среднее арифметическое по входным ценам, как и в исходном советнике.
  • Для снижения риска на волатильных парах измените пороги или отключите мартингейл (UseMartingale = false).
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class ParallaxSellStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ParallaxSellStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}