Ver en GitHub

VR Steals 2 Strategy

This strategy is a StockSharp conversion of the MetaTrader 5 expert "VR---STEALS-2". It opens a single long position and demonstrates simple position management without indicators.

How it works

  1. On start the strategy buys using BuyMarket and records the fill price.
  2. Candle data (1 minute by default) is subscribed via SubscribeCandles.
  3. For each finished candle:
    • When the price has moved Breakeven steps in favour of the trade the stop level is moved above the entry by BreakevenOffset steps.
    • If the price reaches the entry plus TakeProfit steps the position is closed.
    • If the price falls to the stop level (initial StopLoss below entry or the moved breakeven stop) the position is closed.
  4. After exit the strategy does not open new positions.

Parameters

Name Description Default
TakeProfit Distance in price steps to the take-profit level. 50
StopLoss Initial stop distance in price steps. 50
Breakeven Profit in steps required to activate the breakeven stop. 20
BreakevenOffset Offset above entry when the breakeven stop is set. 9
CandleType Candle type used for price processing. 1 minute time frame

StartProtection() is used to enable built-in position protection.

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Opens positions based on SMA trend direction, manages with fixed take profit,
/// stop loss and breakeven levels.
/// </summary>
public class VRSteals2Strategy : Strategy
{
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _breakeven;
	private readonly StrategyParam<decimal> _breakevenOffset;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _entryPrice;
	private decimal _stopPrice;
	private bool _breakevenActivated;
	private decimal _previousFast;
	private decimal _previousSlow;
	private bool _maInitialized;

	public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
	public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
	public decimal Breakeven { get => _breakeven.Value; set => _breakeven.Value = value; }
	public decimal BreakevenOffset { get => _breakevenOffset.Value; set => _breakevenOffset.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public VRSteals2Strategy()
	{
		_takeProfit = Param(nameof(TakeProfit), 50m)
			.SetDisplay("Take Profit", "Distance to take profit in steps", "General");
		_stopLoss = Param(nameof(StopLoss), 50m)
			.SetDisplay("Stop Loss", "Distance to stop loss in steps", "General");
		_breakeven = Param(nameof(Breakeven), 20m)
			.SetDisplay("Breakeven", "Distance to activate breakeven in steps", "General");
		_breakevenOffset = Param(nameof(BreakevenOffset), 9m)
			.SetDisplay("Breakeven Offset", "Offset applied when breakeven is triggered", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to process", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_entryPrice = 0m;
		_stopPrice = 0m;
		_breakevenActivated = false;
		_previousFast = 0m;
		_previousSlow = 0m;
		_maInitialized = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_entryPrice = 0m;
		_breakevenActivated = false;

		var fastSma = new SimpleMovingAverage { Length = 8 };
		var slowSma = new SimpleMovingAverage { Length = 34 };

		var sub = SubscribeCandles(CandleType);
		sub.Bind(fastSma, slowSma, (candle, fast, slow) =>
		{
			if (candle.State != CandleStates.Finished)
				return;

			var price = candle.ClosePrice;
			var step = Security?.PriceStep ?? 1m;

			// Manage existing long position
			if (Position > 0)
			{
				if (!_breakevenActivated && Breakeven > 0m && price >= _entryPrice + Breakeven * step)
				{
					_stopPrice = _entryPrice + BreakevenOffset * step;
					_breakevenActivated = true;
				}

				if (TakeProfit > 0m && price >= _entryPrice + TakeProfit * step)
				{
					SellMarket();
					_entryPrice = 0m;
					_breakevenActivated = false;
					return;
				}

				var stop = _breakevenActivated
					? _stopPrice
					: (StopLoss > 0m ? _entryPrice - StopLoss * step : decimal.MinValue);

				if (price <= stop)
				{
					SellMarket();
					_entryPrice = 0m;
					_breakevenActivated = false;
					return;
				}
			}
			// Manage existing short position
			else if (Position < 0)
			{
				if (!_breakevenActivated && Breakeven > 0m && price <= _entryPrice - Breakeven * step)
				{
					_stopPrice = _entryPrice - BreakevenOffset * step;
					_breakevenActivated = true;
				}

				if (TakeProfit > 0m && price <= _entryPrice - TakeProfit * step)
				{
					BuyMarket();
					_entryPrice = 0m;
					_breakevenActivated = false;
					return;
				}

				var stop = _breakevenActivated
					? _stopPrice
					: (StopLoss > 0m ? _entryPrice + StopLoss * step : decimal.MaxValue);

				if (price >= stop)
				{
					BuyMarket();
					_entryPrice = 0m;
					_breakevenActivated = false;
					return;
				}
			}

			// Entry signals based on SMA cross
			if (Position == 0)
			{
				if (_maInitialized && _previousFast <= _previousSlow && fast > slow)
				{
					BuyMarket();
					_entryPrice = price;
					_breakevenActivated = false;
				}
				else if (_maInitialized && _previousFast >= _previousSlow && fast < slow)
				{
					SellMarket();
					_entryPrice = price;
					_breakevenActivated = false;
				}
			}

			_previousFast = fast;
			_previousSlow = slow;
			_maInitialized = true;
		}).Start();

		StartProtection(
			new Unit(2000m, UnitTypes.Absolute),
			new Unit(1000m, UnitTypes.Absolute));
	}
}