在 GitHub 上查看

VWAP Close 策略

概述

该策略计算收盘价的成交量加权移动平均线(VWMA)。当 VWMA 发生方向变化时,产生潜在的入场或出场信号:

  • 当 VWMA 先下降后在当前K线上转为上行(形成谷底)时,策略会平掉空头并视情况开多。
  • 当 VWMA 先上升后在当前K线上转为下行(形成峰值)时,策略会平掉多头并视情况开空。

参数

  • Period – 用于 VWMA 计算的K线数量。
  • Candle Type – 处理的K线时间框架。
  • Buy Open – 允许开多。
  • Sell Open – 允许开空。
  • Buy Close – 当 VWMA 向下转折时允许平多。
  • Sell Close – 当 VWMA 向上转折时允许平空。

说明

策略使用 StockSharp 的 VolumeWeightedMovingAverage 指标,仅处理已完成的K线。交易数量来自策略的 Volume 属性;开新仓时会自动平掉反向仓位。

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>
/// Strategy based on Volume Weighted Moving Average (VWMA) slope reversals.
/// Opens or closes positions when the VWMA changes direction.
/// </summary>
public class VwapCloseStrategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prev1;
	private decimal? _prev2;

	public int Period { get => _period.Value; set => _period.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public VwapCloseStrategy()
	{
		_period = Param(nameof(Period), 2)
			.SetDisplay("Period", "VWMA calculation period", "Indicator")
			.SetOptimize(2, 5, 1);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
		_prev1 = null;
		_prev2 = null;
	}

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

		_prev1 = null;
		_prev2 = null;

		var vwma = new VolumeWeightedMovingAverage { Length = Period };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_prev1 is null || _prev2 is null)
		{
			_prev2 = _prev1;
			_prev1 = vwmaValue;
			return;
		}

		var prev1 = _prev1.Value;
		var prev2 = _prev2.Value;

		// Open long on valley (VWMA turns up)
		if (prev1 < prev2 && vwmaValue > prev1 && Position <= 0)
			BuyMarket();
		// Open short on peak (VWMA turns down)
		else if (prev1 > prev2 && vwmaValue < prev1 && Position >= 0)
			SellMarket();

		_prev2 = prev1;
		_prev1 = vwmaValue;
	}
}