A Estratégia de Executer AC é um port fiel do StockSharp do assessor especialista MetaTrader 5 "Executer AC". O EA original negocia no Accelerator Oscillator (AC) desenvolvido por Bill Williams e combina suas oscilações de momentum com um framework fixo de stop/limite e um módulo de trailing stop. Esta conversão mantém o comportamento da versão MQL5 enquanto expõe parâmetros fáceis de usar que se integram com a API de alto nível do StockSharp.
Lógica de negociação
A estratégia opera com velas finalizadas do período selecionado e depende dos últimos quatro valores do Accelerator Oscillator:
AC[0] – barra completada mais recente (chamada de ac[1] no código original).
AC[1], AC[2], AC[3] – valores progressivamente mais antigos usados para detecção de padrões.
A árvore de decisão é idêntica ao EA:
Gestão de posição
Posições longas são fechadas quando AC[0] < AC[1] (momentum decrescente).
Posições curtas são fechadas quando AC[0] > AC[1] (momentum crescente).
Uma rotina de trailing stop aperta dinamicamente o stop protetor assim que o preço avança além da distância configurada mais o passo de trailing.
Regras de entrada quando flat
Aceleração altista acima de zero: se AC[0] > 0 e AC[1] > 0 e AC[0] > AC[1] > AC[2], uma compra de mercado é emitida.
Aceleração baixista acima de zero: se AC[0] > 0 e AC[1] > 0 e AC[0] < AC[1] < AC[2] < AC[3], uma venda de mercado é emitida.
Aceleração altista abaixo de zero: se AC[0] < 0 e AC[1] < 0 e AC[0] > AC[1] > AC[2] > AC[3], uma compra de mercado é emitida.
Aceleração baixista abaixo de zero: se AC[0] < 0 e AC[1] < 0 e AC[0] < AC[1] < AC[2], uma venda de mercado é emitida.
Cruzamentos da linha zero: um cruzamento descendente (AC[0] > 0 e AC[1] < 0) aciona uma compra; um cruzamento ascendente (AC[0] < 0 e AC[1] > 0) aciona uma venda.
Os sinais são avaliados apenas após confirmar que as velas estão finalizadas, os valores do indicador estão formados e a negociação está habilitada.
Gestão de risco
Stop-loss e take-profit: distâncias configuráveis (em pips) convertidas em unidades de preço usando o step do instrumento. Os stops são recalculados a cada nova entrada e permanecem inalterados até serem atingidos ou substituídos pelo trailing stop.
Trailing stop: replica a lógica do EA. Quando o lucro não realizado excede TrailingStop + TrailingStep (ambos em pips), o preço do stop é movido para Close - TrailingStop para posições longas e Close + TrailingStop para posições curtas, exigindo a melhora requerida antes de cada passo.
Proteção de posição: o helper integrado StartProtection() é invocado para que o StockSharp proteja contra desconexões inesperadas.
Parâmetros
Parâmetro
Descrição
TradeVolume
Volume de ordem usado para todas as entradas de mercado. Normalizado de acordo com o step de volume e limites do instrumento.
StopLossPips
Distância do stop-loss em pips. Um valor de zero desabilita o stop-loss.
TakeProfitPips
Distância do take-profit em pips. Um valor de zero desabilita o take-profit.
TrailingStopPips
Distância do trailing stop em pips. Defina como zero para desabilitar o trailing.
TrailingStepPips
Lucro adicional mínimo (em pips) necessário antes de mover o trailing stop novamente.
CandleType
Período das velas usadas para calcular o Accelerator Oscillator.
Notas de implementação
A normalização de preços respeita tanto o tamanho do tick da bolsa quanto símbolos Forex de três/cinco dígitos, multiplicando o tamanho do ponto por dez quando apropriado.
O histórico do indicador é mantido em um buffer de tamanho fixo para replicar as comparações originais ac[1] … ac[4] sem recorrer a coleções custosas ou consultas de histórico.
A estratégia sempre sai antes de avaliar novas entradas na mesma vela, correspondendo ao fluxo de controle do EA MQL5 onde instruções return evitam re-entrada imediata.
Os valores do trailing stop atualizam tanto o estado interno de trailing quanto o preço efetivo do stop usado para verificações de stop-loss, garantindo consistência com o comportamento PositionModify do EA.
Dicas de uso
Escolha um período de vela que corresponda ao mercado negociado (o script original era comumente usado em gráficos Forex intradía).
Ajuste as distâncias de stop-loss, take-profit e trailing à volatilidade do instrumento escolhido; valores extremamente apertados podem levar a whipsaws frequentes.
Ative controles de risco no lado do broker conectado quando possível, pois a estratégia depende de saídas pelo lado do software.
Combine com gestão de dinheiro em nível de portfólio se pretender executar múltiplas estratégias simultaneamente.
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()