La estrategia Day Trading PAMXA reproduce el asesor experto de MetaTrader 5 que combina reversiones de momentum del Awesome Oscillator de Bill Williams con un filtro estocástico. El port de StockSharp conserva el diseño multitemporal original:
El bucle de decisión principal se ejecuta en el marco temporal de Velas de señal (por defecto 1 hora).
El Awesome Oscillator se evalúa en un marco temporal separado de Velas AO (por defecto 1 día) para obtener momentum de marco temporal superior.
El oscilador estocástico utiliza su propio marco temporal de Velas estocásticas (por defecto 1 hora) para que los niveles %K/%D estén alineados con los ajustes originales.
La estrategia mantiene como máximo una posición a la vez. Cuando aparece una configuración alcista, primero cubre cualquier corto activo antes de entrar en largo, y viceversa para configuraciones bajistas.
Lógica de entrada
Calcular los valores de Awesome Oscillator más recientes terminados en el marco temporal AO.
Calcular los valores más recientes terminados de %K y %D del oscilador estocástico en el marco temporal estocástico.
En cada vela de señal terminada:
Configuración alcista: se activa cuando la barra AO anterior estaba por debajo de cero y la última barra cerró por encima de cero (reversión de momentum) mientras %K o %D está por debajo del umbral Nivel estocástico bajo (condición de sobreventa). Cualquier corto abierto se cubre y se abre un nuevo largo si no queda posición.
Configuración bajista: se activa cuando la barra AO anterior estaba por encima de cero y la última barra cerró por debajo de cero mientras %K o %D está por encima del umbral Nivel estocástico alto (condición de sobrecompra). Cualquier largo abierto se cierra y, si está plano, se abre una nueva posición corta.
Salida y gestión de riesgo
Un stop-loss y take-profit basados en pips se adjuntan en la entrada. Cuando el mínimo de la vela (para largos) o el máximo (para cortos) supera el nivel de stop, la posición se liquida inmediatamente. La misma lógica aplica al objetivo de beneficio.
Un trailing stop opcional se activa cuando el precio se ha movido Trailing Stop + Trailing Step pips a favor de la posición. Para largos el stop sigue el máximo más alto menos la distancia de trailing; para cortos sigue el mínimo más bajo más la distancia de trailing. El ajuste del trailing ocurre solo cuando el movimiento supera el paso de trailing, replicando el comportamiento original del EA.
La gestión monetaria puede operar en dos modos:
FixedVolume: usa el parámetro Order Volume directamente.
RiskPercent: calcula el volumen de modo que el porcentaje configurado del valor del portafolio se perdería si se alcanza el stop-loss. El cálculo usa la distancia de stop en pips y redondea al paso de volumen más cercano.
La estrategia nunca hace pirámide – una vez que existe una posición, la próxima señal opuesta la aplanará antes de considerar cualquier nueva entrada.
Parámetros
Parámetro
Descripción
Stop Loss
Distancia de stop-loss en pips. Cero desactiva el stop protector.
Take Profit
Distancia de take-profit en pips. Cero desactiva el objetivo de beneficio.
Trailing Stop
Distancia de activación del trailing stop en pips. Cero desactiva el trailing.
Trailing Step
Pips adicionales requeridos antes de que el trailing stop avance. Debe ser positivo cuando el trailing está activado.
Money Mode
Selecciona entre dimensionamiento FixedVolume y RiskPercent.
Money Value
Interpretado como tamaño de lote con volumen fijo, o como porcentaje de riesgo con dimensionamiento basado en riesgo.
Order Volume
Volumen base usado cuando Money Mode es FixedVolume.
Stochastic %K
Longitud del periodo de cálculo estocástico %K.
Stochastic %D
Longitud de suavizado para la línea estocástica %D.
Stochastic Slow
Factor de suavizado final aplicado al oscilador estocástico.
Level Up
Umbral estocástico superior que habilita entradas cortas.
Level Down
Umbral estocástico inferior que habilita entradas largas.
Signal Candles
Marco temporal que impulsa el bucle principal de trading.
Stochastic Candles
Marco temporal que alimenta el oscilador estocástico.
AO Candles
Marco temporal que alimenta el Awesome Oscillator.
AO Fast / AO Slow
Períodos para las medias móviles internas del Awesome Oscillator.
Notas de implementación
El cálculo del valor del pip emula la lógica de MetaTrader: cuando el instrumento usa 3 o 5 decimales, un pip equivale a diez pasos de precio; de lo contrario equivale a un paso de precio.
El oscilador estocástico de StockSharp no expone una selección dedicada de "campo de precio"; el port usa el cálculo basado en cierre predeterminado mientras conserva los parámetros de suavizado configurables.
El manejo del trailing stop se implementa como una verificación virtual en los máximos/mínimos de las velas. Esto replica los ajustes de stop del lado servidor realizados en MetaTrader sin registrar órdenes de stop explícitas.
El código se suscribe a todos los marcos temporales de velas requeridos a través de GetWorkingSecurities, permitiendo al motor solicitar datos para los marcos temporales de señal, estocástico y AO concurrentemente.
Los comentarios en inglés documentan las decisiones de flujo de control más importantes para un mantenimiento más fácil.
Consejos de uso
Alinee el marco temporal de Signal Candles con el marco temporal en el que planea hacer backtesting o trading. Mantenga Stochastic Candles y AO Candles iguales a los valores predeterminados originales cuando quiera replicar exactamente el experto MQL5.
Al cambiar a dimensionamiento RiskPercent, asegúrese de que la distancia de stop-loss sea distinta de cero; de lo contrario la estrategia cae de vuelta a Order Volume.
La configuración de trailing predeterminada refleja el EA original (trailing stop de 25 pips con paso de 5 pips). Establezca Trailing Stop en cero si prefiere un stop-loss estático.
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>
/// Day Trading PAMXA strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class DayTradingPamxaStrategy : 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 DayTradingPamxaStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).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 day_trading_pamxa_strategy(Strategy):
def __init__(self):
super(day_trading_pamxa_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA 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._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(day_trading_pamxa_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(day_trading_pamxa_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * 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 self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * 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 day_trading_pamxa_strategy()