GitHub で見る

Random Hedg Strategy

Overview

The Random Hedg Strategy is a StockSharp high-level port of the MetaTrader expert advisor "Random Hedg". The original EA opens a market buy and market sell simultaneously, then manages both legs with a mixture of fixed stop loss, take profit, break-even and trailing logic. The conversion keeps that core behaviour while exposing every setting as a strategy parameter so the bot can be tuned or optimized directly inside StockSharp Designer.

Trading Logic

  1. Initial hedge – when the strategy is flat it immediately sends two market orders (buy and sell) using the same configurable volume. Both legs receive a stop loss and a take profit expressed in pips.
  2. Break-even guard – after price moves in favour of a leg by the configured number of pips, the stop level is shifted to break even plus an optional offset (long positions) or break even minus the offset (short positions). This mirrors the "move to no loss" toggle from the EA.
  3. Trailing stop – once profit exceeds the trailing distance, the stop follows price. For longs the stop trails the highest price minus the trailing distance; for shorts it trails the lowest price plus the distance.
  4. Protective exits – every leg is closed when its take profit or stop loss is touched. Optionally the strategy can liquidate both legs if candle closes below the lower Bollinger Band, recreating the exit filter from the original code.
  5. Cycle restart – once both legs are closed the strategy resets its internal trackers and waits for the next candle to open a new hedged pair.

Parameters

  • HedgeVolume – volume used to open both hedge legs (default 0.1 contracts).
  • StopLossPips – distance of the protective stop loss (default 200 pips).
  • TakeProfitPips – distance of the take profit (default 200 pips).
  • TrailingStopPips – trailing step applied after a position becomes profitable (default 40 pips).
  • BreakEvenTriggerPips – profit required before moving the stop to break even (default 10 pips).
  • BreakEvenOffsetPips – additional profit locked in when the break-even move happens (default 5 pips).
  • EnableTrailing – enables or disables trailing stop management.
  • EnableBreakEven – enables or disables the break-even feature.
  • EnableExitStrategy – enables the Bollinger Band-based liquidation filter.
  • BollingerPeriod – period of the Bollinger Bands used for the optional exit (default 20 candles).
  • BollingerWidth – width multiplier of the Bollinger Bands (default 2).
  • CandleType – candle data series used to drive the logic (default 30-minute time frame).

Implementation Notes

  • The conversion uses the high-level Strategy API with candle subscriptions and the BindEx mechanism to calculate Bollinger Bands on the fly.
  • Internal state tracks the entry price, volume and dynamic protective levels for each leg. This allows the C# version to mimic the money-management helpers from the original EA without relying on platform-specific order handles.
  • Pending order volumes are tracked separately so that fills can be classified as entries or exits even when buy and sell trades happen back-to-back.
  • The strategy expects a hedging-capable account because it keeps long and short exposure at the same time, just like the source expert advisor.
  • Money-based trailing and percentage take-profit features from the MQL code are intentionally omitted. They depend on broker-specific balance data and were rarely used in practice; the StockSharp version focuses on the core price-action management.

Files

  • CS/RandomHedgStrategy.cs – main C# implementation with detailed English inline comments.
  • README.md – this documentation (English).
  • README_ru.md – Russian translation.
  • README_zh.md – Simplified Chinese translation.
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 RandomHedgStrategy : 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 RandomHedgStrategy()
	{
		_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;
	}
}