Ver en GitHub

HelloSmart Strategy

This strategy implements a simple grid trading approach that opens positions in only one direction. A new order is placed each time the market moves a configured number of ticks against the last entry. When the aggregate position volume reaches a threshold, the next order size is multiplied. All positions are closed when total profit or loss hits predefined limits.

Parameters

  • Trade Direction – choose 1 to open only long positions or 2 to open only short positions.
  • Step – number of price ticks the market must move before adding another position.
  • Initial Lot – base volume for the first order.
  • Threshold Volume – cumulative position size that triggers lot multiplication.
  • Maximum Lot – upper bound for any single order volume.
  • Profit Target – profit amount in currency after which all positions are closed.
  • Loss Limit – loss amount in currency after which all positions are closed.
  • Lot Multiplier – factor applied to the next order when the threshold volume is exceeded.
  • Candle Type – candle series used to measure price movement.
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;

/// <summary>
/// Grid strategy that opens sequential orders in a single direction.
/// Closes all positions on reaching profit or loss limits.
/// </summary>
public class HelloSmartStrategy : Strategy
{
	public enum TradeModes
	{
		Buy,
		Sell
	}

	private readonly StrategyParam<TradeModes> _tradeMode;
	private readonly StrategyParam<decimal> _stepTicks;
	private readonly StrategyParam<decimal> _profitTarget;
	private readonly StrategyParam<decimal> _lossLimit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _lastPrice;

	public TradeModes Mode { get => _tradeMode.Value; set => _tradeMode.Value = value; }
	public decimal StepTicks { get => _stepTicks.Value; set => _stepTicks.Value = value; }
	public decimal ProfitTarget { get => _profitTarget.Value; set => _profitTarget.Value = value; }
	public decimal LossLimit { get => _lossLimit.Value; set => _lossLimit.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public HelloSmartStrategy()
	{
		_tradeMode = Param(nameof(Mode), TradeModes.Sell)
			.SetDisplay("Trade Direction", "Buy or Sell direction", "General");
		_stepTicks = Param(nameof(StepTicks), 300m)
			.SetGreaterThanZero()
			.SetDisplay("Step", "Price movement to add position", "Risk");
		_profitTarget = Param(nameof(ProfitTarget), 60m)
			.SetDisplay("Profit Target", "Close all positions on this profit", "Risk");
		_lossLimit = Param(nameof(LossLimit), 5100m)
			.SetDisplay("Loss Limit", "Close all positions on this loss", "Risk");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

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

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

		_lastPrice = 0m;
	}

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

		_lastPrice = 0m;

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ProcessCandle).Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var price = candle.ClosePrice;
		var stepPrice = StepTicks * 0.01m;

		// Check PnL limits first
		if (Position != 0 && (PnL > ProfitTarget || PnL < -LossLimit))
		{
			if (Position > 0)
				SellMarket();
			else
				BuyMarket();
			_lastPrice = price;
			return;
		}

		if (Mode == TradeModes.Buy)
		{
			var needOpen = Position <= 0 || (Position > 0 && (_lastPrice - price) >= stepPrice);
			if (needOpen)
			{
				BuyMarket();
				_lastPrice = price;
			}
		}
		else
		{
			var needOpen = Position >= 0 || (Position < 0 && (price - _lastPrice) >= stepPrice);
			if (needOpen)
			{
				SellMarket();
				_lastPrice = price;
			}
		}
	}
}