The Day Trading PAMXA strategy reproduces the MetaTrader 5 expert advisor that combines Bill Williams' Awesome Oscillator momentum reversals with a stochastic filter. The StockSharp port keeps the original multi-timeframe design:
The main decision loop runs on the Signal Candles timeframe (default 1 hour).
The Awesome Oscillator is evaluated on a separate AO Candles timeframe (default 1 day) to obtain higher time-frame momentum.
The stochastic oscillator uses its own Stochastic Candles timeframe (default 1 hour) so that %K/%D levels are aligned with the original settings.
The strategy holds at most one position at a time. Whenever a bullish setup appears it first covers any active shorts before entering long, and vice versa for bearish setups.
Entry logic
Compute the most recent finished values of Awesome Oscillator on the AO timeframe.
Compute the most recent finished %K and %D values of the stochastic oscillator on the Stochastic timeframe.
On every finished Signal candle:
Bullish setup: Triggered when the previous AO bar was below zero and the latest bar closed above zero (momentum reversal) while either %K or %D is below the Stochastic Level Down threshold (oversold condition). Any open short is covered and a new long is opened if no position remains.
Bearish setup: Triggered when the previous AO bar was above zero and the latest bar closed below zero while either %K or %D is above the Stochastic Level Up threshold (overbought condition). Any open long is closed and, if flat, a new short position is opened.
Exit and risk management
A pip-based stop loss and take profit are attached at entry. When the candle's low (for longs) or high (for shorts) breaches the stop level the position is liquidated immediately. The same logic applies to the profit target.
An optional trailing stop activates once price has moved by Trailing Stop + Trailing Step pips in favour of the position. For longs the stop follows the highest high minus the trailing distance; for shorts it follows the lowest low plus the trailing distance. The trailing adjustment occurs only when the move exceeds the trailing step, mirroring the original EA behaviour.
Money management can operate in two modes:
FixedVolume: uses the Order Volume parameter directly.
RiskPercent: computes volume so that the configured percentage of portfolio value would be lost if the stop loss is hit. The calculation uses the pip-based stop distance and rounds to the nearest volume step.
The strategy never pyramids – once a position exists the next opposing signal will flatten it before any new entry is considered.
Parameters
Parameter
Description
Stop Loss
Stop-loss distance in pips. Zero disables the protective stop.
Take Profit
Take-profit distance in pips. Zero disables the profit target.
Trailing Stop
Trailing stop activation distance in pips. Zero disables trailing.
Trailing Step
Additional pips required before the trailing stop advances. Must be positive when trailing is enabled.
Money Mode
Selects between FixedVolume and RiskPercent sizing.
Money Value
Interpreted as lot size when using fixed volume, or as risk percentage when using risk-based sizing.
Order Volume
Base volume used when Money Mode is FixedVolume.
Stochastic %K
Lookback length for the stochastic %K calculation.
Stochastic %D
Smoothing length for the stochastic %D line.
Stochastic Slow
Final smoothing factor applied to the stochastic oscillator.
Level Up
Upper stochastic threshold that enables short entries.
Level Down
Lower stochastic threshold that enables long entries.
Signal Candles
Timeframe that drives the main trading loop.
Stochastic Candles
Timeframe feeding the stochastic oscillator.
AO Candles
Timeframe feeding the Awesome Oscillator.
AO Fast / AO Slow
Periods for the internal moving averages of the Awesome Oscillator.
Implementation notes
Pip value calculation emulates the MetaTrader logic: when the security uses 3 or 5 decimal places a pip equals ten price steps; otherwise it equals one price step.
The StockSharp stochastic oscillator does not expose a dedicated "price field" selection; the port uses the default close-based calculation while retaining the configurable smoothing parameters.
Trailing stop handling is implemented as a virtual check on candle highs/lows. This replicates the server-side stop adjustments performed in MetaTrader without registering explicit stop orders.
The code subscribes to all required candle timeframes through GetWorkingSecurities, allowing the engine to request data for the signal, stochastic and AO timeframes concurrently.
English inline comments document the most important control-flow decisions for easier maintenance.
Usage tips
Align the Signal Candles timeframe with the timeframe you plan to backtest or trade on. Keep Stochastic Candles and AO Candles equal to the original defaults when you want to mirror the MQL5 expert exactly.
When switching to RiskPercent sizing ensure the stop-loss distance is non-zero; otherwise the strategy falls back to Order Volume.
The default trailing configuration mirrors the original EA (25 pip trailing stop with 5 pip step). Set Trailing Stop to zero if you prefer a static stop-loss.
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()