在 GitHub 上查看
Breadandbutter2 策略
概述
Breadandbutter2 策略来源于 MQL/7710/Breadandbutter2.mq4 的 MT4 智能交易程序。本实现订阅 1 小时 K 线,并基于开盘价计算三条线性加权移动平均线(LWMA)。当三条均线同时完成顺序反转时,被视为趋势切换,策略立即调整持仓方向,并在趋势持续时按设定节奏加仓。
工作流程
- 订阅 1 小时周期的蜡烛(可通过 Candle Type 参数调整)。
- 计算开盘价的 LWMA(5)、LWMA(10)、LWMA(15)。
- 如果上一根蜡烛满足
LWMA5 < LWMA10 < LWMA15,而当前蜡烛满足 LWMA5 > LWMA10 > LWMA15,则认定为多头信号;反向不等式则为空头信号。
- 多头信号出现时,将目标仓位调整为 Volume 手的多单;空头信号时调整为同等规模的空单。策略只买入或卖出达到目标仓位所需的差额。
- 每次交易后重置 Interval 计数器。如果在没有新信号的情况下累计了 Interval 根已完成蜡烛,则在当前方向上再加一单,并同步更新保护性订单。
- 通过
SetTakeProfit 和 SetStopLoss 为每个仓位设置 Take Profit 和 Stop 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 的符号判断方向。
- 每次下单后都会调用
SetTakeProfit 与 SetStopLoss,使保护性订单与最新仓位保持一致。
备注
- 原始 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class breadandbutter2_strategy(Strategy):
def __init__(self):
super(breadandbutter2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_wma5 = 0.0; self._prev_wma10 = 0.0; self._has_prev = False
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(breadandbutter2_strategy, self).OnReseted()
self._prev_wma5 = 0.0; self._prev_wma10 = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(breadandbutter2_strategy, self).OnStarted2(time)
self._has_prev = False
wma5 = WeightedMovingAverage()
wma5.Length = 5
wma10 = WeightedMovingAverage()
wma10.Length = 10
wma15 = WeightedMovingAverage()
wma15.Length = 15
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(wma5, wma10, wma15, self.process_candle).Start()
def process_candle(self, candle, wma5, wma10, wma15):
if candle.State != CandleStates.Finished: return
w5 = float(wma5); w10 = float(wma10); w15 = float(wma15)
if not self._has_prev:
self._prev_wma5 = w5; self._prev_wma10 = w10; self._has_prev = True; return
if self._prev_wma5 <= self._prev_wma10 and w5 > w10 and w10 > w15 and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_wma5 >= self._prev_wma10 and w5 < w10 and w10 < w15 and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_wma5 = w5; self._prev_wma10 = w10
def CreateClone(self): return breadandbutter2_strategy()