AO Lightning переносит советника MT5 «AO_Lightning» на высокоуровневый API StockSharp. Система отслеживает наклон осциллятора Awesome, рассчитанного по медианным ценам. Когда показатель снижается, стратегия наращивает длинную позицию, а при росте осциллятора выстраивает короткую пирамиду. Максимальное количество слоёв ограничено параметром, при смене направления противоположные позиции закрываются до открытия новых.
Торговая логика
Подписаться на выбранные свечи и вычислить Awesome Oscillator с коротким периодом 5 и длинным периодом 34 (значения взяты из исходного MQL-кода).
Обрабатывать только завершённые свечи, чтобы избежать повторных срабатываний.
Значение AO первой завершённой свечи сохраняется как базовое для сравнения.
Когда текущее значение AO меньше предыдущего:
При наличии открытой короткой позиции отправляется рыночная покупка, которая закрывает весь шорт и одновременно добавляет один длинный слой.
Если шорта нет и текущая длинная экспозиция меньше лимита, добавляется ещё один длинный слой.
Когда текущее значение AO больше предыдущего:
При наличии длинной позиции выставляется рыночная продажа, закрывающая существующий лонг и сразу открывающая один короткий слой.
Если лонга нет и короткая экспозиция меньше лимита, добавляется новый короткий слой.
Если значение AO не изменилось, позиция остаётся без изменений.
Метод StartProtection() активируется один раз при запуске, чтобы в Designer можно было подключить стопы и другие защитные модули.
Логика полностью повторяет оригинальный советник: направление задаёт наклон AO, противоположные сделки закрываются до переворота, а позиции наращиваются слоями до установленного ограничения.
Управление позицией
TradeVolume определяет размер каждого дополнительного слоя и соответствует параметру MT5 LotFixed.
MaxPositions повторяет входной параметр MT5 Orders, ограничивая количество слоёв в каждом направлении.
Пирамидинг линейный: каждый сигнал добавляет ровно один слой указанного объёма, пока лимит не достигнут.
Переворот выполняется одним комбинированным ордером, который закрывает противоположную позицию и сразу открывает новый слой в нужную сторону.
Параметры
Имя
Описание
Значение по умолчанию
TradeVolume
Объём ордера для каждого слоя.
1
MaxPositions
Максимальное число длинных или коротких слоёв.
10
AoShortPeriod
Длина быстрой SMA внутри Awesome Oscillator (по медианной цене).
5
AoLongPeriod
Длина медленной SMA для Awesome Oscillator.
34
CandleType
Тип свечей, используемый стратегией.
Таймфрейм 5 минут
Примечания
В исходном советнике MT5 параметры названы Period_sma_slow и Period_sma_fast, но значения (5 и 34) перепутаны местами. В версии StockSharp использованы интуитивные AoShortPeriod и AoLongPeriod при сохранении поведения.
Python-реализация не создавалась, как и требовалось в задаче.
Тесты не включены; рекомендуется провести нужную проверку в Designer или собственной тестовой среде перед запуском на реальных торгах.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// AO Lightning strategy.
/// Trades based on the Awesome Oscillator momentum slope direction.
/// Buys when AO is rising, sells when AO is falling.
/// </summary>
public class AoLightningStrategy : Strategy
{
private readonly StrategyParam<int> _aoShortPeriod;
private readonly StrategyParam<int> _aoLongPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevAo;
private bool _initialized;
public int AoShortPeriod
{
get => _aoShortPeriod.Value;
set => _aoShortPeriod.Value = value;
}
public int AoLongPeriod
{
get => _aoLongPeriod.Value;
set => _aoLongPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public AoLightningStrategy()
{
_aoShortPeriod = Param(nameof(AoShortPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("AO Fast", "Short SMA period for Awesome Oscillator", "Indicators");
_aoLongPeriod = Param(nameof(AoLongPeriod), 34)
.SetGreaterThanZero()
.SetDisplay("AO Slow", "Long SMA period for Awesome Oscillator", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAo = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevAo = 0;
_initialized = false;
var ao = new AwesomeOscillator
{
ShortMa = { Length = AoShortPeriod },
LongMa = { Length = AoLongPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ao, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ao);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal aoValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_prevAo = aoValue;
_initialized = true;
return;
}
// Buy when AO is rising (bullish momentum)
if (aoValue > _prevAo && Position <= 0)
{
BuyMarket();
}
// Sell when AO is falling (bearish momentum)
else if (aoValue < _prevAo && Position >= 0)
{
SellMarket();
}
_prevAo = aoValue;
}
}
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 AwesomeOscillator
from StockSharp.Algo.Strategies import Strategy
class ao_lightning_strategy(Strategy):
def __init__(self):
super(ao_lightning_strategy, self).__init__()
self._ao_short_period = self.Param("AoShortPeriod", 5) \
.SetDisplay("AO Fast", "Short SMA period for Awesome Oscillator", "Indicators")
self._ao_long_period = self.Param("AoLongPeriod", 34) \
.SetDisplay("AO Slow", "Long SMA period for Awesome Oscillator", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._prev_ao = 0.0
self._initialized = False
@property
def AoShortPeriod(self):
return self._ao_short_period.Value
@property
def AoLongPeriod(self):
return self._ao_long_period.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(ao_lightning_strategy, self).OnReseted()
self._prev_ao = 0.0
self._initialized = False
def OnStarted2(self, time):
super(ao_lightning_strategy, self).OnStarted2(time)
self._prev_ao = 0.0
self._initialized = False
ao = AwesomeOscillator()
ao.ShortMa.Length = self.AoShortPeriod
ao.LongMa.Length = self.AoLongPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(ao, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ao)
self.DrawOwnTrades(area)
def _on_process(self, candle, ao_value):
if candle.State != CandleStates.Finished:
return
av = float(ao_value)
if not self._initialized:
self._prev_ao = av
self._initialized = True
return
if av > self._prev_ao and self.Position <= 0:
self.BuyMarket()
elif av < self._prev_ao and self.Position >= 0:
self.SellMarket()
self._prev_ao = av
def CreateClone(self):
return ao_lightning_strategy()