Открыть на GitHub

Стратегия Rubberbands Safety Net

Портирование советника RUBBERBANDS 1.6 с платформы MetaTrader на высокоуровневый API StockSharp. В исходной версии одновременно поддерживаются встречные покупки и продажи, а закрытая прибыльная сторона сразу замещается новой заявкой. При превышении заданного денежного порога активируется «страховочная сетка». В StockSharp используется модель неттинга, поэтому вместо одновременных разнонаправленных ордеров реализовано усреднение по текущему направлению при сохранении тех же денежных условий.

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

  • Старт цикла. В начале каждой минуты или при включении переключателя Enter Now открывается рыночная позиция объёмом BaseVolume. Направление чередуется: покупка, затем продажа и т.д.
  • Базовая цель по прибыли. Текущий нереализованный результат сравнивается с TargetProfitPerLot * BaseVolume. После достижения порога позиция закрывается, а следующая сделка открывается в обратную сторону.
  • Сессионный контроль. Опции UseSessionTakeProfit и UseSessionStopLoss отслеживают суммарную (реализованную + нереализованную) прибыль в денежном выражении на базовый лот. При достижении порога все позиции закрываются, счётчики сбрасываются.
  • Режим безопасности. Если опция включена и убыток превышает SafetyStartPerLot * BaseVolume, стратегия переходит в режим усреднения и добавляет заявки объёмом SafetyVolume в сторону текущей позиции. Каждый дополнительный убыток на SafetyStepPerLot за лот планирует ещё одно усреднение.
  • Выход из режима безопасности. Позиция полностью закрывается при достижении прибыли SafetyProfitPerLot * |Position| либо при превышении порога SafetyModeTakeProfitPerLot * BaseVolume на уровне всей сессии.

Условия входа

Для покупки

  • Нет открытой позиции и либо завершилась очередная минута, либо включён флаг Enter Now.
  • Очередной цикл должен начинаться с длинной позиции.
  • Флаг Stop Trading выключен.

Для продажи

  • Аналогично условиям покупки, но следующий цикл должен стартовать с короткой позиции.

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

  • Достижение цели по базе. Закрыть текущую позицию и сменить направление следующего цикла.
  • Сессионные цели. Закрыть позицию, обнулить накопленную прибыль и ждать следующего запуска.
  • Прибыль в режиме безопасности. Закрыть позицию при достижении целевого значения PnL в режиме усреднения.
  • Усреднение. Дополнительные заявки добавляются при каждом шаге убытка SafetyStepPerLot.
  • Ручное закрытие. Переключатель Close Now закрывает позицию на следующей свече и обнуляет накопленную прибыль.

Параметры

Параметр Описание
BaseVolume Объём стартовой рыночной заявки.
TargetProfitPerLot Денежная цель по прибыли на базовый лот.
UseSessionTakeProfit / SessionTakeProfitPerLot Включение и настройка сессионного тейк-профита.
UseSessionStopLoss / SessionStopLossPerLot Включение и настройка сессионного стоп-лосса.
UseSafetyMode Включение режима усреднения.
SafetyStartPerLot Убыток на базовый лот для активации режима безопасности.
SafetyVolume Объём каждой усредняющей заявки.
SafetyStepPerLot Дополнительный убыток на лот для постановки следующего усреднения.
SafetyProfitPerLot Цель по прибыли в режиме безопасности.
SafetyModeTakeProfitPerLot Сессионная цель по прибыли при активном режиме безопасности.
UseInitialState, InitialProfitSoFar, InitialSafetyMode, InitialSafetyToBuy, InitialUsedSafetyCount Восстановление состояния при перезапуске.
QuiesceNow, Enter Now, Stop Trading, Close Now Ручные переключатели, аналогичные extern-параметрам советника.
CandleType Таймфрейм свечей, управляющих логикой (по умолчанию 1 минута).

Практические замечания

  • Из-за неттинговой модели StockSharp стратегия усредняет текущую позицию вместо открытия хеджирующих ордеров. Денежные пороги соответствуют настройкам оригинального советника.
  • Все пороговые значения указаны в валюте счёта на один лот. Подберите их с учётом стоимости тика инструмента.
  • Переключатели Stop Trading, Close Now, Enter Now, Quiesce можно менять из интерфейса без изменения кода.
  • В методе OnStarted вызывается StartProtection() для использования стандартных защит StockSharp.
  • Алгоритм автоматически подгоняет объём под шаг и минимальный/максимальный объём инструмента; убедитесь, что соответствующие параметры заданы в инструменте.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Rubberbands Safety Net: Mean reversion with Bollinger-style bands.
/// Buys at lower band, sells at upper band with ATR stops.
/// </summary>
public class RubberbandsSafetyNetStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _smaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _bandMult;

	private decimal _entryPrice;

	public RubberbandsSafetyNetStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_smaLength = Param(nameof(SmaLength), 20)
			.SetDisplay("SMA Length", "SMA center band period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for bands and stops.", "Indicators");

		_bandMult = Param(nameof(BandMult), 2.0m)
			.SetDisplay("Band Mult", "ATR multiplier for bands.", "Signals");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int SmaLength
	{
		get => _smaLength.Value;
		set => _smaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal BandMult
	{
		get => _bandMult.Value;
		set => _bandMult.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_entryPrice = 0;

		var sma = new SimpleMovingAverage { Length = SmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, sma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (atrVal <= 0)
			return;

		var close = candle.ClosePrice;
		var upper = smaVal + atrVal * BandMult;
		var lower = smaVal - atrVal * BandMult;

		if (Position > 0)
		{
			if (close >= smaVal || close <= _entryPrice - atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= smaVal || close >= _entryPrice + atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close <= lower)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close >= upper)
			{
				_entryPrice = close;
				SellMarket();
			}
		}
	}
}