Auf GitHub ansehen

AO Divergenz-Strategie

Die Strategie sucht nach bullischen und bärischen Divergenzen zwischen dem Awesome Oscillator (AO) und dem Preis. Eine bullische Divergenz tritt auf, wenn der Preis ein niedrigeres Tief bildet, während der AO ein höheres Tief bildet. Eine bärische Divergenz erscheint, wenn der Preis ein höheres Hoch bildet, während der AO ein niedrigeres Hoch bildet.

Wird eine bullische Divergenz erkannt, eröffnet die Strategie eine Long-Position. Eine bärische Divergenz löst eine Short-Position aus. Positionen werden bei entgegengesetzten Signalen umgekehrt.

Details

  • Einstiegskriterien: Bullische oder bärische AO-Divergenz mit dem Preis.
  • Long/Short: Beide.
  • Ausstiegskriterien: Entgegengesetztes Divergenzsignal.
  • Stops: Nein.
  • Standardwerte:
    • CandleType = 5 Minuten
    • FastLength = 5
    • SlowLength = 34
    • Lookback = 5
    • UseEma = false
  • Filter:
    • Kategorie: Indikator
    • Richtung: Beide
    • Indikatoren: Awesome Oscillator
    • Stops: Nein
    • Komplexität: Mittel
    • Zeitrahmen: Intraday
    • Saisonalität: Nein
    • Neuronale Netze: Nein
    • Divergenz: Ja
    • Risikolevel: Mittel
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.
/// Buys when AO crosses above zero, sells when AO crosses below zero.
/// Uses EMA as trend filter.
/// </summary>
public class AoDivergenceStrategy : Strategy
{
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevAo;
	private int _barIndex;
	private int _lastTradeBar;

	/// <summary>
	/// EMA trend filter period.
	/// </summary>
	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	/// <summary>
	/// Cooldown bars between trades.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public AoDivergenceStrategy()
	{
		_emaLength = Param(nameof(EmaLength), 40)
			.SetDisplay("EMA Length", "EMA trend filter period", "Indicator");

		_cooldownBars = Param(nameof(CooldownBars), 380)
			.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
		_prevAo = 0;
		_barIndex = 0;
		_lastTradeBar = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var ao = new AwesomeOscillator();

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, ao, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal aoValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_barIndex++;

		var cooldownOk = _barIndex - _lastTradeBar > CooldownBars;

		// AO zero line crossover with EMA trend
		var aoCrossUp = _prevAo <= 0 && aoValue > 0;
		var aoCrossDown = _prevAo >= 0 && aoValue < 0;

		if (aoCrossUp && candle.ClosePrice > emaValue && Position <= 0 && cooldownOk)
		{
			BuyMarket();
			_lastTradeBar = _barIndex;
		}
		else if (aoCrossDown && candle.ClosePrice < emaValue && Position >= 0 && cooldownOk)
		{
			SellMarket();
			_lastTradeBar = _barIndex;
		}

		_prevAo = aoValue;
	}
}