Стратегия EA Template
Эта стратегия основана на шаблоне эксперта MetaTrader. Она анализирует предыдущую завершённую свечу и открывает позицию в направлении её тела. Бычья свеча инициирует покупку, медвежья — продажу. Режим реверса меняет трактовку цвета свечи, позволяя торговать против основного сигнала.
Стратегия поддерживает фиксированный размер позиции или расчёт от капитала. Уровни стоп-лосса и тейк-профита задаются в пунктах от цены входа. Торговля пропускается, если спред превышает допустимое значение.
Детали
- Критерии входа:
- Покупка: предыдущая свеча закрылась выше открытия и
ReverseTradeотключён. - Продажа: предыдущая свеча закрылась ниже открытия и
ReverseTradeотключён. - При включённом
ReverseTradeсигналы инвертируются. - Спред должен быть ниже
SpreadLimitпунктов.
- Покупка: предыдущая свеча закрылась выше открытия и
- Критерии выхода:
- Противоположный цвет свечи или срабатывание стоп-лосса/тейк-профита.
- Размер позиции:
- Фиксированный
Lotsили расчёт от капитала поRiskPercent, когдаUseMoneyManagementравен true.
- Фиксированный
- Стопы:
StopLossиTakeProfitв пунктах относительно входа.
- Направления: длинные и короткие позиции.
- Индикаторы: отсутствуют.
- Уровень риска: средний.
Параметры позволяют настроить тип свечей, режим реверса, правила управления капиталом и ограничения по риску.
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()