Estratégia de Divergência AO
A estratégia busca divergências de alta e de baixa entre o Awesome Oscillator (AO) e o preço. Uma divergência de alta ocorre quando o preço forma uma mínima mais baixa enquanto o AO forma uma mínima mais alta. Uma divergência de baixa aparece quando o preço forma uma máxima mais alta enquanto o AO forma uma máxima mais baixa.
Quando uma divergência de alta é detectada, a estratégia abre uma posição comprada. Uma divergência de baixa aciona uma posição vendida. As posições se invertem com sinais opostos.
Detalhes
- Critérios de entrada: Divergência de alta ou baixa do AO com o preço.
- Comprado/Vendido: Ambos.
- Critérios de saída: Sinal de divergência oposta.
- Stops: Não.
- Valores padrão:
CandleType= 5 minutosFastLength= 5SlowLength= 34Lookback= 5UseEma= false
- Filtros:
- Categoria: Indicador
- Direção: Ambos
- Indicadores: Awesome Oscillator
- Stops: Não
- Complexidade: Intermediário
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Sim
- Nível de risco: Médio
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>
/// AO divergence strategy.
/// The Awesome Oscillator is the difference of a FastLength and a SlowLength moving average (SMA, or EMA when UseEma is set) of
/// the median price. Oscillator swing lows and highs are confirmed as pivots with Lookback bars on each side. A pivot low where
/// price made a lower low than at the previous pivot low while AO made a higher low is a bullish divergence and goes long; a
/// pivot high where price made a higher high while AO made a lower high is a bearish divergence and goes short. The opposite
/// divergence reverses the position.
/// </summary>
public class AoDivergenceStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<bool> _useEma;
private readonly StrategyParam<DataType> _candleType;
private readonly List<(decimal ao, decimal low, decimal high)> _bars = [];
private IIndicator _fastMa;
private IIndicator _slowMa;
private (decimal ao, decimal price)? _lastPivotLow;
private (decimal ao, decimal price)? _lastPivotHigh;
/// <summary>
/// Fast moving average period of AO.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow moving average period of AO.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// Bars on each side that confirm an AO pivot.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// Use EMA instead of SMA for AO.
/// </summary>
public bool UseEma
{
get => _useEma.Value;
set => _useEma.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AoDivergenceStrategy()
{
_fastLength = Param(nameof(FastLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast moving average period of AO", "Indicator");
_slowLength = Param(nameof(SlowLength), 34)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow moving average period of AO", "Indicator");
_lookback = Param(nameof(Lookback), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Bars on each side that confirm an AO pivot", "Divergence");
_useEma = Param(nameof(UseEma), false)
.SetDisplay("Use EMA", "Use EMA instead of SMA for AO", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastMa = null;
_slowMa = null;
ResetState();
}
private void ResetState()
{
_bars.Clear();
_lastPivotLow = null;
_lastPivotHigh = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
_fastMa = UseEma ? new ExponentialMovingAverage { Length = FastLength } : new SimpleMovingAverage { Length = FastLength };
_slowMa = UseEma ? new ExponentialMovingAverage { Length = SlowLength } : new SimpleMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var fast = _fastMa.Process(median, candle.ServerTime, true).ToDecimal();
var slow = _slowMa.Process(median, candle.ServerTime, true).ToDecimal();
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
return;
_bars.Add((fast - slow, candle.LowPrice, candle.HighPrice));
var window = 2 * Lookback + 1;
if (_bars.Count > window)
_bars.RemoveAt(0);
if (_bars.Count < window)
return;
// The middle bar is an AO pivot once Lookback bars on each side have finished.
var (pivotAo, pivotLow, pivotHigh) = _bars[Lookback];
var isPivotLow = true;
var isPivotHigh = true;
for (var i = 0; i < window; i++)
{
if (i == Lookback)
continue;
if (_bars[i].ao <= pivotAo)
isPivotLow = false;
if (_bars[i].ao >= pivotAo)
isPivotHigh = false;
}
var bullish = false;
var bearish = false;
if (isPivotLow)
{
if (_lastPivotLow is (decimal prevAo, decimal prevLow))
bullish = pivotLow < prevLow && pivotAo > prevAo;
_lastPivotLow = (pivotAo, pivotLow);
}
if (isPivotHigh)
{
if (_lastPivotHigh is (decimal prevAo, decimal prevHigh))
bearish = pivotHigh > prevHigh && pivotAo < prevAo;
_lastPivotHigh = (pivotAo, pivotHigh);
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (bullish && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (bearish && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class ao_divergence_strategy(Strategy):
"""
AO divergence strategy.
The Awesome Oscillator is the difference of a FastLength and a SlowLength moving average (SMA, or EMA when UseEma is set) of
the median price. Oscillator swing lows and highs are confirmed as pivots with Lookback bars on each side. A pivot low where
price made a lower low than at the previous pivot low while AO made a higher low is a bullish divergence and goes long; a
pivot high where price made a higher high while AO made a lower high is a bearish divergence and goes short. The opposite
divergence reverses the position.
"""
def __init__(self):
super(ao_divergence_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 5) \
.SetGreaterThanZero() \
.SetDisplay("Fast Length", "Fast moving average period of AO", "Indicator")
self._slow_length = self.Param("SlowLength", 34) \
.SetGreaterThanZero() \
.SetDisplay("Slow Length", "Slow moving average period of AO", "Indicator")
self._lookback = self.Param("Lookback", 5) \
.SetGreaterThanZero() \
.SetDisplay("Lookback", "Bars on each side that confirm an AO pivot", "Divergence")
self._use_ema = self.Param("UseEma", False) \
.SetDisplay("Use EMA", "Use EMA instead of SMA for AO", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._fast_ma = None
self._slow_ma = None
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def _reset_state(self):
self._bars = []
self._last_pivot_low = None
self._last_pivot_high = None
def OnReseted(self):
super(ao_divergence_strategy, self).OnReseted()
self._fast_ma = None
self._slow_ma = None
self._reset_state()
def _create_ma(self, length):
ma = ExponentialMovingAverage() if self._use_ema.Value else SimpleMovingAverage()
ma.Length = length
return ma
def OnStarted2(self, time):
super(ao_divergence_strategy, self).OnStarted2(time)
self._reset_state()
self._fast_ma = self._create_ma(self._fast_length.Value)
self._slow_ma = self._create_ma(self._slow_length.Value)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
median = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
fast = float(to_decimal(process_float(self._fast_ma, median, candle.ServerTime, True)))
slow = float(to_decimal(process_float(self._slow_ma, median, candle.ServerTime, True)))
if not self._fast_ma.IsFormed or not self._slow_ma.IsFormed:
return
self._bars.append((fast - slow, float(candle.LowPrice), float(candle.HighPrice)))
lookback = self._lookback.Value
window = 2 * lookback + 1
if len(self._bars) > window:
self._bars.pop(0)
if len(self._bars) < window:
return
# The middle bar is an AO pivot once Lookback bars on each side have finished.
pivot_ao, pivot_low, pivot_high = self._bars[lookback]
is_pivot_low = True
is_pivot_high = True
for i in range(window):
if i == lookback:
continue
if self._bars[i][0] <= pivot_ao:
is_pivot_low = False
if self._bars[i][0] >= pivot_ao:
is_pivot_high = False
bullish = False
bearish = False
if is_pivot_low:
if self._last_pivot_low is not None:
prev_ao, prev_low = self._last_pivot_low
bullish = pivot_low < prev_low and pivot_ao > prev_ao
self._last_pivot_low = (pivot_ao, pivot_low)
if is_pivot_high:
if self._last_pivot_high is not None:
prev_ao, prev_high = self._last_pivot_high
bearish = pivot_high > prev_high and pivot_ao < prev_ao
self._last_pivot_high = (pivot_ao, pivot_high)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if bullish and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif bearish and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return ao_divergence_strategy()