Открыть на GitHub

Стратегия Hedging Martingale

Обзор

Данная стратегия представляет собой порт MetaTrader-советника "Hedging Martingale" (каталог MQL/23693) на платформу StockSharp. Она поддерживает хедж за счет одновременного открытия длинной и короткой позиции на каждой новой свече, а затем добавляет усредняющие заявки по схеме Мартингейла. При неблагоприятном движении цены на заданное число пунктов к убыточной стороне добавляется новая сделка с увеличенным объемом, в то время как противоположная позиция остается открытой.

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

  • Начальный хедж: если стратегия находится без позиций и формируется новая закрытая свеча, открываются покупки и продажи одинаковым базовым объемом.
  • Шаги Мартингейла: когда цена движется против стороны более чем на Pip Step пунктов, на эту сторону открывается дополнительная сделка. Объем умножается на Volume Multiplier, что воспроизводит наращивание лота в исходном советнике.
  • Индивидуальные тейк-профиты: каждая позиция имеет собственный тейк-профит на расстоянии Take Profit (pips). После достижения указанной дистанции позиции сокращаются обратной сделкой.
  • Закрытие корзины: весь портфель позиций закрывается при достижении денежного целевого профита, процента от стартового капитала или после возврата прибыли сверх допустимой величины в режиме трейлинг-локирования. Эти режимы соответствуют функциям Take_Profit_In_Money, Take_Profit_In_percent и TRAIL_PROFIT_IN_MONEY2 из MQL-версии.
  • Ограничение сделок: параметр Max Trades задает максимальное число одновременно открытых уровней. При включенном Close On Max стратегия полностью закрывает корзину при превышении лимита.

Параметры

Название Описание
Candle Type Таймфрейм, на котором анализируются свечи и принимаются решения.
Use Money TP / Money Take Profit Включение денежного тейк-профита для всей корзины.
Use Percent TP / Percent Take Profit Закрытие корзины при достижении заданного процента от начального капитала.
Enable Trailing / Trailing Start / Trailing Step Настройка денежного трейлинг-лока для плавающей прибыли.
Take Profit (pips) Дистанция тейк-профита для отдельных позиций.
Pip Step Отрицательное движение цены (в пунктах), после которого добавляется следующий уровень.
Base Volume Базовый объем для стартовых покупок и продаж.
Volume Multiplier Множитель объема при открытии новых уровней.
Max Trades Максимальное количество одновременно открытых сделок (в обе стороны).
Close On Max Закрывать ли все позиции при достижении лимита по количеству сделок.

Дополнительно

  • Все заявки выставляются рыночными ордерами через BuyMarket и SellMarket, как и в оригинальном советнике.
  • Объемы нормализуются под шаг лота инструмента, чтобы избежать отказов биржи.
  • После полного выхода из позиций трейлинг-лок сбрасывает накопленный максимум прибыли для новой серии сделок.

Файлы

  • CS/HedgingMartingaleStrategy.cs – реализация стратегии (C#).
  • README.md – описание на английском.
  • README_zh.md – описание на китайском.
  • README_ru.md – описание на русском.
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 HedgingMartingaleStrategy : 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 HedgingMartingaleStrategy()
	{
		_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;
	}
}