Стратегия является портом эксперта MQL5 Exp_PA_Oscillator.mq5. Она использует две экспоненциальные скользящие средние (EMA) по цене закрытия свечей и анализирует производную разницы между ними.
Логика
Рассчитываются быстрая и медленная EMA.
Вычисляется разница между ними и её изменение относительно предыдущего значения.
На основании производной определяется цветовой код:
0 – производная положительная, MACD растёт.
1 – производная равна нулю.
2 – производная отрицательная, MACD падает.
По двум последним закрытым свечам формируются сигналы:
Две свечи назад цвет был 0, а последняя свеча имеет другой цвет → открытие длинной позиции и закрытие короткой.
Две свечи назад цвет был 2, а последняя свеча имеет другой цвет → открытие короткой позиции и закрытие длинной.
Параметры
Имя
Описание
FastLength
Период быстрой EMA.
SlowLength
Период медленной EMA.
BuyPosOpen
Разрешить открытие длинных позиций.
SellPosOpen
Разрешить открытие коротких позиций.
BuyPosClose
Разрешить закрытие длинных позиций.
SellPosClose
Разрешить закрытие коротких позиций.
CandleType
Таймфрейм свечей для расчётов.
Примечания
Обрабатываются только завершённые свечи.
Для входа и выхода используются рыночные заявки.
Реализация предназначена для демонстрационных целей, а не для гарантированной прибыльности.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the PA Oscillator indicator.
/// It trades when the derivative of the difference between fast and slow EMAs changes sign.
/// </summary>
public class PaOscillatorStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevMacd;
private int? _prevColor;
private int? _prevPrevColor;
/// <summary>
/// Length of the fast EMA.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Length of the slow EMA.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public PaOscillatorStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length", "Fast EMA period", "Indicators");
_slowLength = Param(nameof(SlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Length", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = null;
_prevColor = null;
_prevPrevColor = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
var macd = fast - slow;
if (_prevMacd is null)
{
_prevMacd = macd;
_prevColor = 1;
_prevPrevColor = 1;
return;
}
var osc = macd - _prevMacd.Value;
var color = osc > 0 ? 0 : osc < 0 ? 2 : 1;
if (_prevPrevColor == 0 && _prevColor > 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (_prevPrevColor == 2 && _prevColor < 2)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevMacd = macd;
_prevPrevColor = _prevColor;
_prevColor = color;
}
}
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 pa_oscillator_strategy(Strategy):
def __init__(self):
super(pa_oscillator_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast EMA Length", "Fast EMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow EMA Length", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._prev_macd = None
self._prev_color = None
self._prev_prev_color = None
@property
def fast_length(self):
return self._fast_length.Value
@property
def slow_length(self):
return self._slow_length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pa_oscillator_strategy, self).OnReseted()
self._prev_macd = None
self._prev_color = None
self._prev_prev_color = None
def OnStarted2(self, time):
super(pa_oscillator_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.fast_length
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.slow_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast = float(fast)
slow = float(slow)
macd = fast - slow
if self._prev_macd is None:
self._prev_macd = macd
self._prev_color = 1
self._prev_prev_color = 1
return
osc = macd - self._prev_macd
if osc > 0:
color = 0
elif osc < 0:
color = 2
else:
color = 1
if self._prev_prev_color == 0 and self._prev_color > 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif self._prev_prev_color == 2 and self._prev_color < 2:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_macd = macd
self._prev_prev_color = self._prev_color
self._prev_color = color
def CreateClone(self):
return pa_oscillator_strategy()