Ver no GitHub

Lilith Goes To Hollywood Strategy

Overview

This strategy recreates the behaviour of the MetaTrader expert "Lilith goes to Hollywood" inside the StockSharp high level API. It implements a hedging grid that can operate in two very different modes:

  • Automated mode – Parabolic SAR triggers immediate market entries whenever price crosses the stop-and-reverse value.
  • Manual mode – Pending stop/limit orders are parked around user-defined reference prices and left to fill.

In both cases the strategy keeps track of the long and short exposure separately, calculates the floating PnL of the open grid and uses that information to decide when to deploy additional recovery orders.

Operating modes

  • Automated – When no position is open the strategy subscribes to the Parabolic SAR indicator (0.02/0.2 factors). If the candle close is above the indicator it buys at market, if it is below it sells. The executed price becomes the new focus and recovery stops are armed at a configurable anchor distance around it.
  • Manual – When no position is open the strategy submits a single pending order per side. If the market trades below the buy level a buy stop is created, otherwise a buy limit is submitted. The sell side mirrors the same logic around the PriceDown level. Once one of the orders fills the other side remains active until cancelled manually or by the strategy.

Order management logic

  • The grid keeps running totals of filled long/short volumes and pending buy/sell orders. This allows the strategy to measure imbalances between both sides of the book.
  • Whenever the floating profit reaches the dynamic target (account value / 1000) the strategy closes every position and cancels all pending orders.
  • If the floating PnL drops below -AccountValue * RiskPercent / 100, an emergency hedge is deployed by opening market orders that cover the net short or long excess.
  • Recovery orders are expressed as stop orders placed around the focus price (automated mode) or around the configured manual prices. Their size is calculated as (opposite exposure * XFactor) - current exposure, mimicking the MT4 logic of oversizing the next order to rebalance the grid.

Parameters

Name Description
Automated Enables Parabolic SAR driven market entries. Disable to work in manual pending order mode.
PriceUp Reference price used to create buy stop/limit orders in manual mode.
PriceDown Reference price used to create sell stop/limit orders in manual mode.
AnchorSteps Distance, expressed in price steps, used to offset recovery orders from the focus price.
ManualVolume Base lot size when operating manually or when the dynamic position sizing produces zero.
XFactor Multiplier applied to the opposing exposure when sizing recovery orders.
RiskPercent Maximum floating loss (percentage of the account value) tolerated before the strategy deploys an emergency hedge.
CandleType Time-frame used to drive the Parabolic SAR and general management logic.

Risk controls

  • Profit taking is dynamic and scales with the account value, providing an automatic way to raise the target as the account grows.
  • Emergency hedging can neutralise extreme drawdowns by flattening the most exposed side of the grid once the floating loss exceeds the RiskPercent threshold.
  • All pending orders are rounded to the instrument tick size and volumes are adjusted to respect exchange limits, matching the typical protections of the original MetaTrader expert.

Conversion notes

  • MetaTrader ticks are replaced with finished candles. The default one-minute timeframe keeps the strategy reactive, yet it can be adjusted via the CandleType parameter.
  • The Anchor setting from the MQL source expressed the distance in points. Here it is configured as a number of price steps so it adapts to the instrument tick size automatically.
  • The original "Comment" output has been converted into strategy log messages (LogInfo) so the platform journal contains the same feedback without relying on chart annotations.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Lilith Goes To Hollywood: SMA crossover with RSI filter and ATR stops.
/// </summary>
public class LilithGoesToHollywoodStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastSmaLength;
	private readonly StrategyParam<int> _slowSmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

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

		_fastSmaLength = Param(nameof(FastSmaLength), 10)
			.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");

		_slowSmaLength = Param(nameof(SlowSmaLength), 25)
			.SetDisplay("Slow SMA", "Slow SMA period.", "Indicators");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

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

	public int FastSmaLength
	{
		get => _fastSmaLength.Value;
		set => _fastSmaLength.Value = value;
	}

	public int SlowSmaLength
	{
		get => _slowSmaLength.Value;
		set => _slowSmaLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

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

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastSmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowSmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, rsi, atr, ProcessCandle)
			.Start();

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 45)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 55)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}