在 GitHub 上查看

Darvas Boxes 系统策略

概述

该策略基于经典的 Darvas Boxes 概念,采用突破交易方式。利用 Donchian Channels 指标构建动态价格区间。当价格收盘高于箱体上边界时开多单;当价格收盘低于箱体下边界时开空单。同时可以设置止损和止盈来管理风险。

工作原理

  1. 对于每根 K 线,Donchian Channels 根据参数 BoxPeriod 计算上下边界。
  2. 策略跟踪上一根 K 线的边界值以检测突破。
  3. 如果当前收盘价突破上一根 K 线的上边界:
    • 如果允许,平掉现有空单。
    • 如果允许,开立新的多单。
  4. 如果当前收盘价跌破上一根 K 线的下边界:
    • 如果允许,平掉现有多单。
    • 如果允许,开立新的空单。
  5. 对持仓持续监控,达到止损或止盈条件时退出。

参数

  • BoxPeriod (int): 构建箱体所使用的 K 线数量,默认 20。
  • StopLoss (decimal): 入场价到止损位的距离,默认 1000。
  • TakeProfit (decimal): 入场价到止盈位的距离,默认 2000。
  • AllowBuyEntry (bool): 是否允许开多,默认 true
  • AllowSellEntry (bool): 是否允许开空,默认 true
  • AllowBuyExit (bool): 是否允许在反向信号或风险事件下平多,默认 true
  • AllowSellExit (bool): 是否允许在反向信号或风险事件下平空,默认 true
  • CandleType (DataType): 用于计算的 K 线类型,默认 4 小时 K 线。

使用方法

  1. 将策略附加到交易标的并设置参数。
  2. 启动策略,系统会订阅所需的 K 线并开始处理数据。
  3. 当出现突破信号时,策略会以市价单执行交易。
  4. 止损和止盈用于管理持仓风险。

说明

  • 策略使用 BindEx 连接指标与 K 线数据,属于高层 API 的实现。
  • 不使用额外的内部集合,所有计算基于回调提供的值完成。
  • 仅处理已完成的 K 线以确保信号可靠。
  • 按要求,代码中的注释为英文。
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>
/// Darvas Boxes breakout strategy using Donchian Channels.
/// Opens long position when price breaks above the upper box line.
/// Opens short position when price breaks below the lower box line.
/// </summary>
public class DarvasBoxesSystemStrategy : Strategy
{
	private readonly StrategyParam<int> _boxPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevUpper;
	private decimal _prevLower;
	private decimal _prevClose;

	public int BoxPeriod
	{
		get => _boxPeriod.Value;
		set => _boxPeriod.Value = value;
	}

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

	public DarvasBoxesSystemStrategy()
	{
		_boxPeriod = Param(nameof(BoxPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Box Period", "Period for box calculation", "Indicators")
			.SetOptimize(10, 40, 5);

		_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();
		_prevUpper = 0m;
		_prevLower = 0m;
		_prevClose = 0m;
	}

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

		_prevUpper = 0m;
		_prevLower = 0m;
		_prevClose = 0m;

		var donchian = new DonchianChannels { Length = BoxPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(donchian, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (value is not IDonchianChannelsValue box)
			return;

		if (box.UpperBand is not decimal upper || box.LowerBand is not decimal lower)
			return;

		if (_prevUpper == 0m)
		{
			_prevUpper = upper;
			_prevLower = lower;
			_prevClose = candle.ClosePrice;
			return;
		}

		var isUpBreakout = candle.ClosePrice > _prevUpper && _prevClose <= _prevUpper;
		var isDownBreakout = candle.ClosePrice < _prevLower && _prevClose >= _prevLower;

		if (isUpBreakout && Position <= 0)
			BuyMarket();
		else if (isDownBreakout && Position >= 0)
			SellMarket();

		_prevUpper = upper;
		_prevLower = lower;
		_prevClose = candle.ClosePrice;
	}
}