在 GitHub 上查看

Breadandbutter2 策略

概述

Breadandbutter2 策略来源于 MQL/7710/Breadandbutter2.mq4 的 MT4 智能交易程序。本实现订阅 1 小时 K 线,并基于开盘价计算三条线性加权移动平均线(LWMA)。当三条均线同时完成顺序反转时,被视为趋势切换,策略立即调整持仓方向,并在趋势持续时按设定节奏加仓。

工作流程

  1. 订阅 1 小时周期的蜡烛(可通过 Candle Type 参数调整)。
  2. 计算开盘价的 LWMA(5)、LWMA(10)、LWMA(15)。
  3. 如果上一根蜡烛满足 LWMA5 < LWMA10 < LWMA15,而当前蜡烛满足 LWMA5 > LWMA10 > LWMA15,则认定为多头信号;反向不等式则为空头信号。
  4. 多头信号出现时,将目标仓位调整为 Volume 手的多单;空头信号时调整为同等规模的空单。策略只买入或卖出达到目标仓位所需的差额。
  5. 每次交易后重置 Interval 计数器。如果在没有新信号的情况下累计了 Interval 根已完成蜡烛,则在当前方向上再加一单,并同步更新保护性订单。
  6. 通过 SetTakeProfitSetStopLoss 为每个仓位设置 Take ProfitStop Loss(以价格步长表示)。当参数为 0 时,关闭对应的保护措施。

参数说明

参数 默认值 说明
Volume 0.1 初始入场及每次加仓的手数。
Take Profit 20 止盈距离(价格步长)。0 表示关闭。
Stop Loss 20 止损距离(价格步长)。0 表示关闭。
Interval 4 在再次加仓前需要等待的完成蜡烛数量。0 表示不加仓。
Cross Filter 1.1 保留自原始脚本的 ADX 过滤阈值,目前未启用。
Candle Type 1 小时时间框架 计算 LWMA 所使用的蜡烛类型。

仓位管理

  • AdjustPosition 方法确保在每次信号后仓位精确达到目标规模。
  • 加仓逻辑仅在已有仓位方向上执行,并根据 Position 的符号判断方向。
  • 每次下单后都会调用 SetTakeProfitSetStopLoss,使保护性订单与最新仓位保持一致。

备注

  • 原始 MT4 代码计算了 ADX,但未实际使用;因此在转换中保留 Cross Filter 参数以便未来扩展。
  • 原脚本中的间隔计数被注释掉,本版本按照原始注释意图实现了基于完成蜡烛的加仓节奏。
  • OnStarted 中调用 StartProtection(),以启用 StockSharp 的内置风险控制机制。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bread and Butter strategy - triple WMA crossover.
/// Buys when WMA(5) crosses above WMA(10) and WMA(10) is above WMA(15).
/// Sells when WMA(5) crosses below WMA(10) and WMA(10) is below WMA(15).
/// </summary>
public class Breadandbutter2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevWma5;
	private decimal _prevWma10;
	private bool _hasPrev;

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

	public Breadandbutter2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevWma5 = 0m; _prevWma10 = 0m; _hasPrev = false; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var wma5 = new WeightedMovingAverage { Length = 5 };
		var wma10 = new WeightedMovingAverage { Length = 10 };
		var wma15 = new WeightedMovingAverage { Length = 15 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(wma5, wma10, wma15, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevWma5 = wma5;
			_prevWma10 = wma10;
			_hasPrev = true;
			return;
		}

		if (_prevWma5 <= _prevWma10 && wma5 > wma10 && wma10 > wma15 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevWma5 >= _prevWma10 && wma5 < wma10 && wma10 < wma15 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevWma5 = wma5;
		_prevWma10 = wma10;
	}
}