Estrategia de Divergencia AO
La estrategia busca divergencias alcistas y bajistas entre el Awesome Oscillator (AO) y el precio. Una divergencia alcista ocurre cuando el precio forma un mínimo más bajo mientras el AO forma un mínimo más alto. Una divergencia bajista aparece cuando el precio forma un máximo más alto mientras el AO forma un máximo más bajo.
Cuando se detecta una divergencia alcista, la estrategia abre una posición larga. Una divergencia bajista activa una posición corta. Las posiciones se revierten con señales opuestas.
Detalles
- Criterios de entrada: Divergencia alcista o bajista del AO con el precio.
- Largo/Corto: Ambos.
- Criterios de salida: Señal de divergencia opuesta.
- Stops: No.
- Valores predeterminados:
CandleType= 5 minutosFastLength= 5SlowLength= 34Lookback= 5UseEma= false
- Filtros:
- Categoría: Indicador
- Dirección: Ambos
- Indicadores: Awesome Oscillator
- Stops: No
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí
- Nivel de riesgo: Medio
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()