Auf GitHub ansehen

Strategie Volume Weighted Price Breakout

Diese Strategie kombiniert einen gleitenden Durchschnitt mit einem volumengewichteten gleitenden Durchschnitt (VWMA). Wenn der Preis über dem VWMA handelt, deutet das darauf hin, dass Käufer dominant sind. Ein Ausbruch tritt auf, wenn der Preis den VWMA von der entgegengesetzten Seite kreuzt.

Tests zeigen eine durchschnittliche jährliche Rendite von etwa 40%. Am besten funktioniert es im Kryptomarkt.

Trades orientieren sich an der VWMA-Richtung und verwenden den einfachen gleitenden Durchschnitt als übergeordneten Trendfilter. Ausstiege erfolgen, wenn der Preis relativ zum gleitenden Durchschnitt dreht.

Das Ziel ist, Ausbrüche zu erfassen, die durch Volumen unterstützt werden.

Details

  • Einstiegskriterien: Preis über oder unter VWMA mit MA-Bestätigung.
  • Long/Short: Beide Richtungen.
  • Ausstiegskriterien: Preis kreuzt MA in entgegengesetzter Richtung oder Stop.
  • Stops: Ja.
  • Standardwerte:
    • MAPeriod = 20
    • VWAPPeriod = 20
    • CandleType = TimeSpan.FromMinutes(5)
  • Filter:
    • Kategorie: Ausbruch
    • Richtung: Beide
    • Indikatoren: VWMA, MA
    • Stops: Ja
    • Komplexität: Mittel
    • Zeitrahmen: Intraday
    • Saisonalität: Nein
    • Neuronale Netze: Nein
    • Divergenz: Nein
    • 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>
/// Volume Weighted Price Breakout Strategy.
/// Long entry: Price rises above VWMA.
/// Short entry: Price falls below VWMA.
/// Exit: Price crosses MA in the opposite direction.
/// </summary>
public class VolumeWeightedPriceBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<int> _vwapPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private int _cooldown;

	/// <summary>
	/// MA Period.
	/// </summary>
	public int MAPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// VWAP Period.
	/// </summary>
	public int VWAPPeriod
	{
		get => _vwapPeriod.Value;
		set => _vwapPeriod.Value = value;
	}

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

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

	/// <summary>
	/// Initialize <see cref="VolumeWeightedPriceBreakoutStrategy"/>.
	/// </summary>
	public VolumeWeightedPriceBreakoutStrategy()
	{
		_maPeriod = Param(nameof(MAPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Period for Moving Average", "Indicators")
			.SetOptimize(10, 50, 10);

		_vwapPeriod = Param(nameof(VWAPPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("VWAP Period", "Period for VWMA", "Indicators")
			.SetOptimize(10, 30, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_cooldownBars = Param(nameof(CooldownBars), 500)
			.SetRange(1, 1000)
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_cooldown = default;
	}

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

		_cooldown = 0;

		var ma = new SimpleMovingAverage { Length = MAPeriod };
		var vwma = new VolumeWeightedMovingAverage { Length = VWAPPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ma, vwma, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		if (Position == 0)
		{
			if (candle.ClosePrice > vwmaValue)
			{
				BuyMarket();
				_cooldown = CooldownBars;
			}
			else if (candle.ClosePrice < vwmaValue)
			{
				SellMarket();
				_cooldown = CooldownBars;
			}
		}
		else if (Position > 0 && candle.ClosePrice < maValue)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position < 0 && candle.ClosePrice > maValue)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
	}
}