Ver en GitHub

Strategy Base Template

This folder provides a minimal scaffold for building custom trading ideas. The strategy only calculates a single exponential moving average and exposes a wide range of common parameters: enabling long or short trades, optional take profit and stop loss, and optimization ranges. Developers can insert their own entry and exit logic inside the placeholders to rapidly prototype new systems.

The template also demonstrates how to start the built‑in protection module with percentage‑based targets, making it easy to experiment with different risk settings. Because no real signals are included, this script is not meant to be traded as‑is but rather to serve as a starting point for further research.

Details

  • Entry Criteria: Not implemented – replace with custom rules.
  • Long/Short: Configurable via parameters.
  • Exit Criteria: Not implemented – replace with custom rules.
  • Stops: Optional percent take profit and stop loss handled by protection module.
  • Default Values:
    • EMA length = 10.
    • Take profit = 1.2%, Stop loss = 1.8% (disabled by default).
  • Filters:
    • Category: Template
    • Direction: Configurable
    • Indicators: EMA
    • Stops: Optional
    • Complexity: Low
    • Timeframe: Any
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: User defined
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

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

/// <summary>
/// Strategy Base - EMA trend following strategy.
/// Buys when price crosses above EMA, sells when price crosses below EMA.
/// </summary>
public class StratBaseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _cooldownBars;

	private ExponentialMovingAverage _ema;

	private decimal _prevClose;
	private decimal _prevEma;
	private int _cooldownRemaining;

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

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	public StratBaseStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period", "Moving Averages");

		_cooldownBars = Param(nameof(CooldownBars), 10)
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
	}

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

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

		_ema = null;
		_prevClose = 0;
		_prevEma = 0;
		_cooldownRemaining = 0;
	}

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

		_ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_ema, OnProcess)
			.Start();

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

	private void OnProcess(ICandleMessage candle, decimal emaVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_ema.IsFormed)
		{
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		if (_prevClose == 0 || _prevEma == 0)
		{
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		// Price crosses above EMA
		var crossUp = candle.ClosePrice > emaVal && _prevClose <= _prevEma;
		// Price crosses below EMA
		var crossDown = candle.ClosePrice < emaVal && _prevClose >= _prevEma;

		if (crossUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume);
			_cooldownRemaining = CooldownBars;
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Math.Abs(Position));
			SellMarket(Volume);
			_cooldownRemaining = CooldownBars;
		}

		_prevClose = candle.ClosePrice;
		_prevEma = emaVal;
	}
}