Ver en GitHub

The strategy replicates the Firebird v0.60 envelope reversion expert. It measures a simple moving average and offsets it by a percentage to form upper and lower envelopes. When price pierces the upper band the strategy sells, and when the lower band breaks it buys. Additional positions are averaged in only if price moves at least one configurable pip step beyond the previous entry. The total stop loss is shared among all entries, preventing runaway trends from repeatedly re-entering in the same direction.

Details

  • Entry Criteria:
    • Calculate an SMA on either candle opens or the high/low midpoint.
    • Upper envelope = SMA × (1 + Percent/100); lower envelope = SMA × (1 − Percent/100).
    • Enter short on a close above the upper band (unless a recent stop locked shorts), enter long on a close below the lower band (unless longs are locked).
    • Average-in trades are allowed once price moves PipStep pips (optionally scaled by power) beyond the latest fill.
  • Long/Short: Long and short.
  • Exit Criteria:
    • Shared take profit at the averaged entry price ± TakeProfit pips.
    • Shared stop loss at the averaged entry price ∓ StopLoss / position count pips.
    • Blocking flag prevents re-entry in the same direction until an opposite signal triggers after a stop.
  • Stops: Yes, aggregated stop loss and take profit.
  • Default Values:
    • MaLength = 10
    • Percent = 0.3
    • TradeOnFriday = true
    • UseHighLow = false (use opens)
    • PipStep = 30
    • IncreasementPower = 0
    • TakeProfit = 30
    • StopLoss = 200
    • TradeVolume = 1
  • Filters:
    • Category: Mean reversion
    • Direction: Both
    • Indicators: SMA envelopes
    • Stops: Yes
    • Complexity: Medium
    • Timeframe: Any
    • Seasonality: Optional Friday filter
    • Neural networks: No
    • Divergence: No
    • Risk level: High due to averaging
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>
/// Firebird MA Envelope Exhaustion strategy - Bollinger Bands mean reversion.
/// Buys when close drops below lower band (exhaustion).
/// Sells when close rises above upper band (exhaustion).
/// Exits at the middle band.
/// </summary>
public class FirebirdMaEnvelopeExhaustionStrategy : Strategy
{
	private readonly StrategyParam<int> _bbPeriod;
	private readonly StrategyParam<decimal> _bbWidth;
	private readonly StrategyParam<DataType> _candleType;

	public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
	public decimal BbWidth { get => _bbWidth.Value; set => _bbWidth.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FirebirdMaEnvelopeExhaustionStrategy()
	{
		_bbPeriod = Param(nameof(BbPeriod), 10)
			.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");

		_bbWidth = Param(nameof(BbWidth), 2m)
			.SetDisplay("BB Width", "Bollinger Bands width", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); }

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

		var bb = new BollingerBands { Length = BbPeriod, Width = BbWidth };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(bb, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!value.IsFinal || value.IsEmpty)
			return;

		var bbVal = value as BollingerBandsValue;
		if (bbVal == null)
			return;

		var upper = bbVal.UpBand;
		var lower = bbVal.LowBand;
		var middle = bbVal.MovingAverage;

		if (upper == null || lower == null || middle == null)
			return;

		var close = candle.ClosePrice;

		// Close below lower band = exhaustion, buy
		if (close < lower.Value && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Close above upper band = exhaustion, sell
		else if (close > upper.Value && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}