Day Trading PAMXA — это порт на StockSharp популярного эксперта MetaTrader 5, который объединяет развороты осциллятора Awesome Oscillator и фильтр по стохастику. В версии для StockSharp сохранена многофреймовая структура:
Главный цикл принятия решений работает на таймфрейме Signal Candles (по умолчанию 1 час).
Awesome Oscillator рассчитывается на отдельном таймфрейме AO Candles (по умолчанию 1 день), что позволяет учитывать старший импульс.
Стохастик использует собственный таймфрейм Stochastic Candles (по умолчанию 1 час), чтобы %K/%D совпадали с оригинальными настройками.
Стратегия всегда держит не более одной позиции. При появлении бычьего сигнала сначала закрываются все открытые шорты, затем открывается лонг. Для медвежьего сигнала логика зеркальна.
Логика входа
Получить последние завершённые значения Awesome Oscillator на таймфрейме AO.
Получить последние завершённые значения %K и %D стохастика на его таймфрейме.
На каждом завершённом сигналом свечи таймфрейма Signal выполняется проверка:
Бычий сигнал: предыдущий столбик AO был ниже нуля, а текущий закрылся выше нуля, и при этом %K или %D опустились ниже порога Stochastic Level Down. Если шортов нет — открывается лонг.
Медвежий сигнал: предыдущий столбик AO был выше нуля, текущий закрылся ниже нуля, и %K или %D поднялись выше порога Stochastic Level Up. Если лонгов нет — открывается шорт.
Выход и управление рисками
Стоп-лосс и тейк-профит задаются в пипсах. При пробое минимума свечи (для лонга) или максимума (для шорта) ниже/выше стоп-уровня позиция закрывается. Аналогично проверяется цель.
Трейлинг-стоп активируется после прохождения ценой расстояния Trailing Stop + Trailing Step в сторону позиции. Для лонга стоп подтягивается под максимум свечи минус дистанция, для шорта — над минимум свечи плюс дистанция. Перенос выполняется только когда прирост превышает шаг трейлинга, как в MetaTrader.
Money Mode позволяет выбрать фиксированный объём (FixedVolume) либо риск-процент (RiskPercent). В режиме RiskPercent объём рассчитывается так, чтобы при срабатывании стопа потерять заданный процент от стоимости портфеля (учитывается расстояние до стопа и шаг объёма инструмента).
Усреднения и пирамидинга нет — новый сигнал сначала закрывает противоположную позицию.
Параметры
Параметр
Назначение
Stop Loss
Дистанция стоп-лосса в пипсах (0 — отключить).
Take Profit
Дистанция тейк-профита в пипсах (0 — отключить).
Trailing Stop
Дистанция активации трейлинг-стопа в пипсах (0 — выключить трейлинг).
Trailing Step
Минимальный шаг в пипсах для переноса трейлинга (должен быть >0 при включённом трейлинге).
Money Mode
Режим управления объёмом: фиксированный объём или риск-процент.
Money Value
Лот в режиме FixedVolume или процент риска в режиме RiskPercent.
Order Volume
Базовый объём сделок при фиксированном режиме.
Stochastic %K
Длина расчёта линии %K.
Stochastic %D
Длина сглаживания линии %D.
Stochastic Slow
Дополнительное сглаживание стохастика.
Level Up
Верхний уровень стохастика для поиска шорта.
Level Down
Нижний уровень стохастика для поиска лонга.
Signal Candles
Таймфрейм основного цикла сигналов.
Stochastic Candles
Таймфрейм для расчёта стохастика.
AO Candles
Таймфрейм для расчёта Awesome Oscillator.
AO Fast / AO Slow
Периоды скользящих внутри AO.
Особенности реализации
Размер пипса рассчитывается как в MetaTrader: для инструментов с 3 или 5 знаками после запятой пип равен десяти минимальным шагам цены, иначе одному шагу.
Стандартный индикатор StochasticOscillator в StockSharp не поддерживает выбор ценового поля Low/High, поэтому используется расчёт по закрытию, но все периоды и сглаживание остаются настраиваемыми.
Трейлинг реализован виртуально — сравниваются экстремумы свечей, что повторяет серверные модификации стопа в MetaTrader без фактической регистрации стоп-заявок.
Метод GetWorkingSecurities подписывает стратегию на три таймфрейма, чтобы движок заранее запросил все необходимые данные.
В коде добавлены поясняющие комментарии на английском языке, упрощающие поддержку.
Рекомендации по использованию
Подбирайте Signal Candles под нужный вам торговый таймфрейм. Чтобы максимально повторить MQL5-версию, оставьте Stochastic Candles и AO Candles по умолчанию.
При выборе режима RiskPercent убедитесь, что стоп-лосс больше нуля, иначе стратегия перейдёт к фиксированному объёму.
Стандартные значения трейлинга (25/5 пипсов) соответствуют оригинальному эксперту. При необходимости отключите его, установив Trailing Stop в ноль.
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()