La Estrategia de Executer AC es un port fiel de StockSharp del asesor experto MetaTrader 5 "Executer AC". El EA original opera sobre el Accelerator Oscillator (AC) desarrollado por Bill Williams y combina sus oscilaciones de momentum con un marco fijo de stop/límite y un módulo de trailing stop. Esta conversión mantiene el comportamiento de la versión MQL5 al tiempo que expone parámetros fáciles de usar que se integran con la API de alto nivel de StockSharp.
Lógica de trading
La estrategia opera con velas completadas del marco temporal seleccionado y se basa en los últimos cuatro valores del Accelerator Oscillator:
AC[0] – barra completada más reciente (llamada ac[1] en el código original).
AC[1], AC[2], AC[3] – valores progresivamente más antiguos usados para la detección de patrones.
El árbol de decisiones es idéntico al EA:
Gestión de posición
Las posiciones largas se cierran cuando AC[0] < AC[1] (momentum decreciente).
Las posiciones cortas se cierran cuando AC[0] > AC[1] (momentum creciente).
Una rutina de trailing stop ajusta dinámicamente el stop protector una vez que el precio supera la distancia configurada más el paso de trailing.
Reglas de entrada cuando está plano
Aceleración alcista por encima de cero: si AC[0] > 0 y AC[1] > 0 y AC[0] > AC[1] > AC[2], se emite una compra de mercado.
Aceleración bajista por encima de cero: si AC[0] > 0 y AC[1] > 0 y AC[0] < AC[1] < AC[2] < AC[3], se emite una venta de mercado.
Aceleración alcista por debajo de cero: si AC[0] < 0 y AC[1] < 0 y AC[0] > AC[1] > AC[2] > AC[3], se emite una compra de mercado.
Aceleración bajista por debajo de cero: si AC[0] < 0 y AC[1] < 0 y AC[0] < AC[1] < AC[2], se emite una venta de mercado.
Cruces de la línea cero: un cruce descendente (AC[0] > 0 y AC[1] < 0) activa una compra, mientras que un cruce ascendente (AC[0] < 0 y AC[1] > 0) activa una venta.
Las señales se evalúan solo después de confirmar que las velas están completadas, los valores del indicador están formados y el trading está habilitado.
Gestión de riesgos
Stop-loss y take-profit: distancias configurables (en pips) convertidas a unidades de precio usando el paso del instrumento. Los stops se recalculan en cada nueva entrada y permanecen sin cambios hasta que se alcancen o sean reemplazados por el trailing stop.
Trailing stop: replica la lógica del EA. Cuando el beneficio no realizado supera TrailingStop + TrailingStep (ambos en pips), el precio del stop se mueve a Close - TrailingStop para posiciones largas y Close + TrailingStop para posiciones cortas, exigiendo la mejora requerida antes de cada paso.
Protección de posición: se invoca el helper integrado StartProtection() para que StockSharp proteja contra desconexiones inesperadas.
Parámetros
Parámetro
Descripción
TradeVolume
Volumen de orden usado para todas las entradas de mercado. Se normaliza según el paso de volumen y los límites del instrumento.
StopLossPips
Distancia del stop-loss en pips. Un valor de cero deshabilita el stop-loss.
TakeProfitPips
Distancia del take-profit en pips. Un valor de cero deshabilita el take-profit.
TrailingStopPips
Distancia del trailing stop en pips. Establezca en cero para deshabilitar el trailing.
TrailingStepPips
Beneficio adicional mínimo (en pips) requerido antes de mover el trailing stop nuevamente.
CandleType
Marco temporal de las velas usadas para calcular el Accelerator Oscillator.
Notas de implementación
La normalización de precios respeta tanto el tamaño del tick de la bolsa como los símbolos Forex de tres/cinco dígitos multiplicando el tamaño del punto por diez cuando corresponde.
El historial del indicador se mantiene en un buffer de tamaño fijo para replicar las comparaciones originales ac[1] … ac[4] sin recurrir a colecciones costosas o consultas de historial.
La estrategia siempre sale antes de evaluar nuevas entradas en la misma vela, coincidiendo con el flujo de control del EA MQL5 donde las instrucciones return previenen la re-entrada inmediata.
Los valores del trailing stop actualizan tanto el estado de trailing interno como el precio efectivo del stop usado para las comprobaciones de stop-loss, asegurando consistencia con el comportamiento PositionModify del EA.
Consejos de uso
Elija un marco temporal de velas que coincida con el mercado que opera (el script original se usaba comúnmente en gráficos Forex intradía).
Ajuste las distancias de stop-loss, take-profit y trailing a la volatilidad del instrumento elegido; valores extremadamente ajustados pueden provocar whipsaws frecuentes.
Habilite controles de riesgo en el lado del broker conectado cuando sea posible, ya que la estrategia depende de salidas del lado del software.
Combine con gestión de dinero a nivel de cartera si pretende ejecutar múltiples estrategias simultáneamente.
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>
/// Executer AC strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExecuterAcStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ExecuterAcStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class executer_ac_strategy(Strategy):
"""
Executer AC strategy using EMA crossover with SL/TP and cooldown.
Buys when fast EMA crosses above slow EMA, sells on reverse.
"""
def __init__(self):
super(executer_ac_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Trading timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(executer_ac_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(executer_ac_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self._fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return executer_ac_strategy()