在 GitHub 上查看
BrainTrend2 V2 Duplex 策略
概述
BrainTrend2 V2 Duplex 策略是 MetaTrader 5 专家顾问 Exp_BrainTrend2_V2_Duplex 在 StockSharp 平台上的高阶移植版本。策略并行运行两个 BrainTrend2 V2 指标实例:一个专门监控做多机会,另一个负责做空。两条通道都可以拥有独立的蜡烛类型、ATR 周期以及信号偏移,方便实现多周期确认或不对称风险配置。
BrainTrend2 V2 是一种趋势跟随模型,通过比较最近的真实波幅与加权 ATR 均值来构建动态“河道”。指标会为蜡烛染色,共有五种颜色:
- 0 – 上升河道中的多头蜡烛。
- 1 – 上升河道中的空头蜡烛。
- 2 – 河道切换时的过渡颜色。
- 3 – 下降河道中的多头蜡烛。
- 4 – 下降河道中的空头蜡烛。
策略通过识别颜色的切换来产生入场与离场信号,完全复刻 MQL5 原版的交易规则。
交易逻辑
多头规则
- 观察
Long Signal Bar 指定的历史蜡烛(默认 1 表示上一根已收盘的蜡烛)。
- 当以下条件满足时开多:
SignalBar + 1(向前两根蜡烛)的颜色 小于 2(绿色调代表上升河道),并且
SignalBar 的颜色 大于 1(从纯多头状态转变)。
- 当
SignalBar + 1 的颜色 大于 2(洋红色调代表下降河道)时平多。
空头规则
- 观察
Short Signal Bar 指定的历史蜡烛。
- 当以下条件满足时开空:
SignalBar + 1 的颜色 大于 2(洋红色调),并且
SignalBar 的颜色 大于 0(除纯多头蜡烛之外的任何颜色)。
- 当
SignalBar + 1 的颜色 小于 2(回到上升河道)时平空。
若开仓方向与当前仓位相反,策略会先自动对冲已有仓位,然后再按设置的交易量下单。
风险管理
- 多头与空头均可单独设置止损和止盈点数,数值为
0 时表示禁用。
- 止损/止盈会根据合约的报价步长换算为绝对价格。多头监控蜡烛的最低价与最高价,空头则监控最高价与最低价,从而模拟盘中触发。
- 交易量以合约单位表示,长短两侧可设置不同数值。
- 策略调用
StartProtection(),可与 StockSharp 的组合级安全机制协同使用。
参数
| 参数 |
说明 |
默认值 |
Long Candle Type |
多头指标使用的蜡烛类型(时间框架)。 |
H4 |
Long ATR Period |
多头通道的 BrainTrend2 ATR 周期。 |
7 |
Long Signal Bar |
多头信号使用的历史偏移。 |
1 |
Enable Long Entries |
是否允许开多。 |
true |
Enable Long Exits |
是否允许多头根据指标平仓。 |
true |
Long Volume |
多头开仓的基础交易量。 |
1 |
Long Stop Loss |
多头止损点数(0 = 禁用)。 |
1000 |
Long Take Profit |
多头止盈点数(0 = 禁用)。 |
2000 |
Short Candle Type |
空头指标使用的蜡烛类型。 |
H4 |
Short ATR Period |
空头通道的 BrainTrend2 ATR 周期。 |
7 |
Short Signal Bar |
空头信号使用的历史偏移。 |
1 |
Enable Short Entries |
是否允许开空。 |
true |
Enable Short Exits |
是否允许空头根据指标平仓。 |
true |
Short Volume |
空头开仓的基础交易量。 |
1 |
Short Stop Loss |
空头止损点数(0 = 禁用)。 |
1000 |
Short Take Profit |
空头止盈点数(0 = 禁用)。 |
2000 |
使用建议
- 通过增大
Signal Bar 可以等待更多蜡烛确认,或为多空两侧配置不同的时间框架以实现多周期过滤。
- 自定义 BrainTrend2 指标完全在 C# 内实现,无需额外的 MQL 文件或库。
- 策略在每根蜡烛收盘时检查止损和止盈。若需要更精细的风险控制,请选择较小的时间框架。
- 当止损与止盈均为 0 时,仓位只会在出现相反颜色信号时平仓。
- 指标需要累计不少于 ATR 周期数量的蜡烛后才会输出有效信号,在此之前策略不会交易。
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>
/// BrainTrend2 V2 Duplex strategy (simplified).
/// Uses ATR-based channel breakout for trend detection.
/// </summary>
public class BrainTrend2V2DuplexStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _channelMult;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public decimal ChannelMult
{
get => _channelMult.Value;
set => _channelMult.Value = value;
}
public BrainTrend2V2DuplexStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_atrPeriod = Param(nameof(AtrPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR length", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA length for trend", "Indicators");
_channelMult = Param(nameof(ChannelMult), 2.5m)
.SetGreaterThanZero()
.SetDisplay("Channel Mult", "ATR multiplier for channel width", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ema, (ICandleMessage candle, decimal atrValue, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var upper = emaValue + ChannelMult * atrValue;
var lower = emaValue - ChannelMult * atrValue;
// Buy when close breaks above upper channel
if (close > upper && Position <= 0)
{
BuyMarket();
}
// Sell when close breaks below lower channel
else if (close < lower && Position >= 0)
{
SellMarket();
}
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 AverageTrueRange, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class brain_trend2_v2_duplex_strategy(Strategy):
def __init__(self):
super(brain_trend2_v2_duplex_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._atr_period = self.Param("AtrPeriod", 7) \
.SetDisplay("ATR Period", "ATR length", "Indicators")
self._ema_period = self.Param("EmaPeriod", 14) \
.SetDisplay("EMA Period", "EMA length for trend", "Indicators")
self._channel_mult = self.Param("ChannelMult", 2.5) \
.SetDisplay("Channel Mult", "ATR multiplier for channel width", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrPeriod(self):
return self._atr_period.Value
@property
def EmaPeriod(self):
return self._ema_period.Value
@property
def ChannelMult(self):
return self._channel_mult.Value
def OnStarted2(self, time):
super(brain_trend2_v2_duplex_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(atr, ema, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, atr_value, ema_value):
if candle.State != CandleStates.Finished:
return
av = float(atr_value)
ev = float(ema_value)
close = float(candle.ClosePrice)
mult = float(self.ChannelMult)
upper = ev + mult * av
lower = ev - mult * av
if close > upper and self.Position <= 0:
self.BuyMarket()
elif close < lower and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return brain_trend2_v2_duplex_strategy()