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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class strat_base_strategy(Strategy):
"""Strategy Base - EMA trend following strategy."""
def __init__(self):
super(strat_base_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period", "Moving Averages")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ema = None
self._prev_close = 0.0
self._prev_ema = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(strat_base_strategy, self).OnReseted()
self._ema = None
self._prev_close = 0.0
self._prev_ema = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(strat_base_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
self._prev_close = float(candle.ClosePrice)
self._prev_ema = float(ema_val)
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = float(candle.ClosePrice)
self._prev_ema = float(ema_val)
return
close = float(candle.ClosePrice)
ema = float(ema_val)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_ema = ema
return
if self._prev_close == 0.0 or self._prev_ema == 0.0:
self._prev_close = close
self._prev_ema = ema
return
cooldown = int(self._cooldown_bars.Value)
cross_up = close > ema and self._prev_close <= self._prev_ema
cross_down = close < ema and self._prev_close >= self._prev_ema
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_ema = ema
def CreateClone(self):
return strat_base_strategy()