EA Template Strategy
This strategy originates from a MetaTrader EA template. It analyses the previous finished candle and opens a position in the direction of the candle body. A bullish candle triggers a long trade, while a bearish candle triggers a short one. Reverse mode flips the interpretation of the candle so the strategy trades against the bar color.
The strategy supports fixed position size or an equity based calculation. Stop-loss and take-profit levels are set in points from the entry price. Trading is skipped when the spread exceeds the allowed threshold.
Details
- Entry Criteria:
- Long: previous candle close > open and
ReverseTradedisabled. - Short: previous candle close < open and
ReverseTradedisabled. - When
ReverseTradeis enabled the signals are inverted. - Spread must be below
SpreadLimitpoints.
- Long: previous candle close > open and
- Exit Criteria:
- Opposite candle color or stop-loss/take-profit hit.
- Position Sizing:
- Fixed size
Lotsor equity based size usingRiskPercentwhenUseMoneyManagementis true.
- Fixed size
- Stops:
StopLossandTakeProfitin points relative to entry price.
- Long/Short: Both directions.
- Indicators: None.
- Risk Level: Medium.
Parameters allow tuning candle type, reverse mode, money management rules and risk limits.
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>
/// Simple candle color strategy converted from a MetaTrader expert advisor.
/// Opens a long position after a bullish candle and a short position after a bearish one.
/// Optional reverse mode flips entry and exit signals.
/// </summary>
public class EaTemplateStrategy : Strategy
{
private readonly StrategyParam<bool> _reverseTrade;
private readonly StrategyParam<bool> _useMoneyManagement;
private readonly StrategyParam<decimal> _riskPercent;
private readonly StrategyParam<decimal> _lots;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _spreadLimit;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _prevClose;
private decimal _prevSma;
private int _barsSinceTrade;
/// <summary>
/// Invert entry and exit signals.
/// </summary>
public bool ReverseTrade
{
get => _reverseTrade.Value;
set => _reverseTrade.Value = value;
}
/// <summary>
/// Use equity based volume calculation.
/// </summary>
public bool UseMoneyManagement
{
get => _useMoneyManagement.Value;
set => _useMoneyManagement.Value = value;
}
/// <summary>
/// Percent of equity to risk per trade.
/// </summary>
public decimal RiskPercent
{
get => _riskPercent.Value;
set => _riskPercent.Value = value;
}
/// <summary>
/// Fixed order size.
/// </summary>
public decimal Lots
{
get => _lots.Value;
set => _lots.Value = value;
}
/// <summary>
/// Stop loss in points.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in points.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Maximum allowed spread in points.
/// </summary>
public int SpreadLimit
{
get => _spreadLimit.Value;
set => _spreadLimit.Value = value;
}
/// <summary>
/// Minimum number of bars between entries.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="EaTemplateStrategy"/>.
/// </summary>
public EaTemplateStrategy()
{
_reverseTrade = Param(nameof(ReverseTrade), false)
.SetDisplay("Reverse Trade", "Invert entry and exit signals", "Trading");
_useMoneyManagement = Param(nameof(UseMoneyManagement), false)
.SetDisplay("Use Money Management", "Use risk based volume calculation", "Risk Management");
_riskPercent = Param(nameof(RiskPercent), 30m)
.SetRange(1m, 100m)
.SetDisplay("Risk Percent", "Percent of equity to risk", "Risk Management");
_lots = Param(nameof(Lots), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Fixed Lot Size", "Fixed order size", "Risk Management");
_stopLoss = Param(nameof(StopLoss), 50)
.SetDisplay("Stop Loss", "Stop loss in points", "Risk Management");
_takeProfit = Param(nameof(TakeProfit), 70)
.SetDisplay("Take Profit", "Take profit in points", "Risk Management");
_spreadLimit = Param(nameof(SpreadLimit), 10)
.SetDisplay("Spread Limit", "Maximum spread in points", "Trading");
_cooldownBars = Param(nameof(CooldownBars), 8)
.SetDisplay("Cooldown Bars", "Minimum number of bars between entries", "Trading");
_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();
_entryPrice = 0m;
_prevClose = 0m;
_prevSma = 0m;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
var sma = new SimpleMovingAverage { Length = 50 };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal sma)
{
if (candle.State != CandleStates.Finished)
return;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
_barsSinceTrade++;
if (sma == 0m)
return;
if (Position == 0)
{
var crossAbove = _prevClose != 0m && _prevSma != 0m && _prevClose <= _prevSma && candle.ClosePrice > sma;
var crossBelow = _prevClose != 0m && _prevSma != 0m && _prevClose >= _prevSma && candle.ClosePrice < sma;
var buySignal = _barsSinceTrade >= CooldownBars && ((isBullish && crossAbove && !ReverseTrade) || (isBearish && crossBelow && ReverseTrade));
var sellSignal = _barsSinceTrade >= CooldownBars && ((isBearish && crossBelow && !ReverseTrade) || (isBullish && crossAbove && ReverseTrade));
if (buySignal)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_barsSinceTrade = 0;
}
else if (sellSignal)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_barsSinceTrade = 0;
}
_prevClose = candle.ClosePrice;
_prevSma = sma;
return;
}
var exitLong = (isBearish && !ReverseTrade) || (isBullish && ReverseTrade);
var exitShort = (isBullish && !ReverseTrade) || (isBearish && ReverseTrade);
if (Position > 0 && exitLong)
SellMarket();
if (Position < 0 && exitShort)
BuyMarket();
if (Position > 0)
CheckStopsForLong(candle.ClosePrice);
else if (Position < 0)
CheckStopsForShort(candle.ClosePrice);
_prevClose = candle.ClosePrice;
_prevSma = sma;
}
private decimal GetOrderVolume(decimal price)
{
if (UseMoneyManagement)
{
var portfolioValue = Portfolio.CurrentValue ?? 0m;
var size = portfolioValue * (RiskPercent / 100m) / price;
return size > 0 ? size : Lots;
}
return Lots;
}
private void CheckStopsForLong(decimal price)
{
var stop = StopLoss * (Security.PriceStep ?? 1m);
if (stop > 0 && price <= _entryPrice - stop)
{
SellMarket();
return;
}
var profit = TakeProfit * (Security.PriceStep ?? 1m);
if (profit > 0 && price >= _entryPrice + profit)
SellMarket();
}
private void CheckStopsForShort(decimal price)
{
var stop = StopLoss * (Security.PriceStep ?? 1m);
if (stop > 0 && price >= _entryPrice + stop)
{
BuyMarket();
return;
}
var profit = TakeProfit * (Security.PriceStep ?? 1m);
if (profit > 0 && price <= _entryPrice - profit)
BuyMarket();
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ea_template_strategy(Strategy):
def __init__(self):
super(ea_template_strategy, self).__init__()
self._reverse_trade = self.Param("ReverseTrade", False) \
.SetDisplay("Reverse Trade", "Invert entry and exit signals", "Trading")
self._use_money_management = self.Param("UseMoneyManagement", False) \
.SetDisplay("Use Money Management", "Use risk based volume calculation", "Risk Management")
self._risk_percent = self.Param("RiskPercent", 30.0) \
.SetDisplay("Risk Percent", "Percent of equity to risk", "Risk Management")
self._lots = self.Param("Lots", 0.1) \
.SetGreaterThanZero() \
.SetDisplay("Fixed Lot Size", "Fixed order size", "Risk Management")
self._stop_loss_param = self.Param("StopLoss", 50) \
.SetDisplay("Stop Loss", "Stop loss in points", "Risk Management")
self._take_profit_param = self.Param("TakeProfit", 70) \
.SetDisplay("Take Profit", "Take profit in points", "Risk Management")
self._spread_limit = self.Param("SpreadLimit", 10) \
.SetDisplay("Spread Limit", "Maximum spread in points", "Trading")
self._cooldown_bars = self.Param("CooldownBars", 8) \
.SetDisplay("Cooldown Bars", "Minimum number of bars between entries", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
self._prev_close = 0.0
self._prev_sma = 0.0
self._bars_since_trade = 0
@property
def reverse_trade(self):
return self._reverse_trade.Value
@property
def use_money_management(self):
return self._use_money_management.Value
@property
def risk_percent(self):
return self._risk_percent.Value
@property
def lots(self):
return self._lots.Value
@property
def stop_loss(self):
return self._stop_loss_param.Value
@property
def take_profit(self):
return self._take_profit_param.Value
@property
def spread_limit(self):
return self._spread_limit.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ea_template_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_close = 0.0
self._prev_sma = 0.0
self._bars_since_trade = self.cooldown_bars
def OnStarted2(self, time):
super(ea_template_strategy, self).OnStarted2(time)
self.StartProtection(None, None)
sma = SimpleMovingAverage()
sma.Length = 50
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle, sma):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma)
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
is_bullish = close > open_price
is_bearish = close < open_price
self._bars_since_trade += 1
if sma_val == 0.0:
return
rev = self.reverse_trade
if self.Position == 0:
cross_above = (self._prev_close != 0.0 and self._prev_sma != 0.0 and
self._prev_close <= self._prev_sma and close > sma_val)
cross_below = (self._prev_close != 0.0 and self._prev_sma != 0.0 and
self._prev_close >= self._prev_sma and close < sma_val)
buy_signal = (self._bars_since_trade >= self.cooldown_bars and
((is_bullish and cross_above and not rev) or (is_bearish and cross_below and rev)))
sell_signal = (self._bars_since_trade >= self.cooldown_bars and
((is_bearish and cross_below and not rev) or (is_bullish and cross_above and rev)))
if buy_signal:
self.BuyMarket()
self._entry_price = close
self._bars_since_trade = 0
elif sell_signal:
self.SellMarket()
self._entry_price = close
self._bars_since_trade = 0
self._prev_close = close
self._prev_sma = sma_val
return
exit_long = (is_bearish and not rev) or (is_bullish and rev)
exit_short = (is_bullish and not rev) or (is_bearish and rev)
if self.Position > 0 and exit_long:
self.SellMarket()
if self.Position < 0 and exit_short:
self.BuyMarket()
step = self.Security.PriceStep if self.Security.PriceStep is not None else 1.0
step = float(step)
if self.Position > 0:
stop = self.stop_loss * step
if stop > 0 and close <= self._entry_price - stop:
self.SellMarket()
else:
profit = self.take_profit * step
if profit > 0 and close >= self._entry_price + profit:
self.SellMarket()
elif self.Position < 0:
stop = self.stop_loss * step
if stop > 0 and close >= self._entry_price + stop:
self.BuyMarket()
else:
profit = self.take_profit * step
if profit > 0 and close <= self._entry_price - profit:
self.BuyMarket()
self._prev_close = close
self._prev_sma = sma_val
def CreateClone(self):
return ea_template_strategy()