Estrategia de Experto en Órdenes (1916)
Esta estrategia abre una posición de mercado cuando el precio del instrumento alcanza niveles predefinidos. Imita el comportamiento del experto MQL original que gestionaba órdenes mediante líneas en el gráfico.
Cómo funciona
- Se suscribe a velas de un marco temporal configurable.
- Cuando el precio de cierre cruza los umbrales
BuyLeveloSellLevel, abre una posición larga o corta de mercado. - Los valores de stop-loss y take-profit se calculan desde el precio de entrada usando
StopLossPipyTakeProfitPip. - Un trailing stop opcional mueve el stop-loss hacia el precio actual a medida que se mueve en una dirección favorable.
Parámetros
- TakeProfitPip – distancia desde el precio de entrada al take profit en pips.
- StopLossPip – distancia desde el precio de entrada al stop loss en pips.
- EnableTrailingStop – habilitar o deshabilitar la lógica de trailing stop.
- CandleType – tipo de vela utilizado para cálculos.
- BuyLevel – nivel de precio que activa la entrada larga (0 deshabilita).
- SellLevel – nivel de precio que activa la entrada corta (0 deshabilita).
Notas
- La estrategia utiliza API de alto nivel y procesa solo velas terminadas.
- El subsistema de protección se activa al inicio para evitar posiciones grandes accidentales.
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>
/// Order expert strategy that uses EMA crossover to enter positions
/// and manages them with stop-loss, take-profit and trailing stop.
/// </summary>
public class OrderExpertStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _slowEma;
private decimal _prevFast;
private decimal _prevSlow;
private int _barsSinceTrade;
private bool _isFirst = true;
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }
public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OrderExpertStrategy()
{
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevFast = 0;
_prevSlow = 0;
_barsSinceTrade = CooldownBars;
_isFirst = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
_slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, ProcessCandle)
.Start();
StartProtection(
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, _slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast)
{
if (candle.State != CandleStates.Finished)
return;
_barsSinceTrade++;
var slowResult = _slowEma.Process(candle.ClosePrice, candle.OpenTime, true);
if (!slowResult.IsFormed)
return;
var slow = slowResult.ToDecimal();
if (_isFirst)
{
_prevFast = fast;
_prevSlow = slow;
_isFirst = false;
return;
}
// EMA cross up -> buy
if (_prevFast <= _prevSlow && fast > slow && Position == 0 && _barsSinceTrade >= CooldownBars)
{
BuyMarket();
_barsSinceTrade = 0;
}
// EMA cross down -> sell
else if (_prevFast >= _prevSlow && fast < slow && Position == 0 && _barsSinceTrade >= CooldownBars)
{
SellMarket();
_barsSinceTrade = 0;
}
_prevFast = fast;
_prevSlow = slow;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class order_expert_strategy(Strategy):
def __init__(self):
super(order_expert_strategy, self).__init__()
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._fast_ema_period = self.Param("FastEmaPeriod", 12) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema_period = self.Param("SlowEmaPeriod", 26) \
.SetGreaterThanZero() \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetGreaterThanZero() \
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._slow_ema = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._bars_since_trade = 0
self._is_first = True
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def fast_ema_period(self):
return self._fast_ema_period.Value
@property
def slow_ema_period(self):
return self._slow_ema_period.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(order_expert_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._bars_since_trade = self.cooldown_bars
self._is_first = True
def OnStarted2(self, time):
super(order_expert_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.fast_ema_period
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.slow_ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, self.process_candle).Start()
self.StartProtection(
Unit(float(self.take_profit_pct), UnitTypes.Percent),
Unit(float(self.stop_loss_pct), UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, self._slow_ema)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast):
if candle.State != CandleStates.Finished:
return
self._bars_since_trade += 1
slow_result = process_float(self._slow_ema, candle.ClosePrice, candle.OpenTime, True)
if not slow_result.IsFormed:
return
slow = float(slow_result)
f = float(fast)
if self._is_first:
self._prev_fast = f
self._prev_slow = slow
self._is_first = False
return
if self._prev_fast <= self._prev_slow and f > slow and self.Position == 0 and self._bars_since_trade >= self.cooldown_bars:
self.BuyMarket()
self._bars_since_trade = 0
elif self._prev_fast >= self._prev_slow and f < slow and self.Position == 0 and self._bars_since_trade >= self.cooldown_bars:
self.SellMarket()
self._bars_since_trade = 0
self._prev_fast = f
self._prev_slow = slow
def CreateClone(self):
return order_expert_strategy()