在 GitHub 上查看
Bulls & Bears Power Average 策略
概述
- 基于
MQL/22016 中的 MetaTrader 5 专家顾问 MySystem.mq5 改写而成。
- 通过计算 K 线最高价/最低价与 EMA 的差值来获得 Elder Bulls Power 与 Bears Power,再将二者求平均以判定动能反转。
- 当平均值仍在零轴下方但开始上升时开多单;当平均值仍在零轴上方却开始下降时开空单。
- 始终保持单一持仓;止损与止盈以点(pip)表示,可按需关闭。
指标逻辑
| 模块 |
说明 |
| 指数移动平均 (EMA) |
对收盘价进行平滑,长度由 MaPeriod 参数控制(默认 5)。 |
| Bulls Power(派生) |
计算公式 High - EMA,衡量多方相对 EMA 的力度。 |
| Bears Power(派生) |
计算公式 Low - EMA,衡量空方相对 EMA 的力度。 |
| 平均动能 |
(Bulls Power + Bears Power) / 2,与上一根已完成 K 线的数值比较判断动量变化。 |
策略仅在 K 线收盘后进行评估,避免在未完成的 K 线中频繁波动。
入场条件
- EMA 必须已经形成(至少处理
MaPeriod 根 K 线)。
- 对最新收盘的 K 线计算 Bulls Power 与 Bears Power。
- 求平均值得到当前的动能读数。
- 与上一次的平均值比较:
- 多头信号:上一值
< 当前值,并且当前值 < 0。若当前无持仓则市价买入。
- 空头信号:上一值
> 当前值,并且当前值 > 0。若当前无持仓则市价卖出。
- 策略本身不产生额外的平仓条件,持仓由止损、止盈或人工干预退出。
风险管理
StopLossPips:止损距离(点)。为 0 表示不启用。根据交易品种的 PriceStep 转换为价格。
TakeProfitPips:止盈距离(点)。为 0 表示不启用。
- 开仓后立即调用
StartProtection 注册防护单,并使用市价成交方式。
参数说明
| 参数 |
默认值 |
说明 |
OrderVolume |
0.1 |
每次下单的数量。 |
StopLossPips |
15 |
止损距离(点)。0 表示关闭。 |
TakeProfitPips |
95 |
止盈距离(点)。0 表示关闭。 |
MaPeriod |
5 |
计算 Bulls/Bears Power 的 EMA 周期。 |
CandleType |
1 小时 |
计算所使用的 K 线类型,可根据数据源调整。 |
使用建议
- 在策略中选择目标品种,并确认
CandleType 与期望的时间框架一致。
- 根据账户规则调整下单量与止损/止盈距离。
- 启动策略后会自动订阅 K 线、计算 EMA,并在满足条件时发出市价订单。
- 策略在已有仓位时不会再次入场,直到仓位被止损/止盈或手动平仓。
- 原始 MQL 参数
InpBarCurrent = 1 在此版本中固定为使用最新两根已收盘 K 线,不进行盘中重新计算。
与原始 MQL 策略的差异
- 采用 StockSharp 高阶
Strategy API,通过订阅 K 线与绑定指标实现逻辑,避免手动读取指针缓冲区。
- 使用
PriceStep 自动换算点值,无需自行判断三位/五位报价。
- 忽略了源代码中被注释掉的平仓逻辑,改用框架自带的保护机制。
- 保留了“一次仅持有一笔仓位”的约束,忠实复刻原始行为。
测试建议
- 使用包含高/低价的历史数据在目标品种与时间框架上回测,确保 Bulls/Bears Power 计算准确。
- 在真实交易前核对品种的最小变动价位与下单步长,确保止损/止盈设置有效。
- 可针对不同波动率尝试调整
MaPeriod,寻找更适合的敏感度。
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>
/// Bulls Bears Power Average strategy. Uses EMA with bulls/bears power crossover.
/// </summary>
public class BullsBearsPowerAverageStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal? _prevPower;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public BullsBearsPowerAverageStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 13).SetGreaterThanZero().SetDisplay("EMA Period", "EMA lookback for power calc", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevPower = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevPower = null;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal)
{
if (candle.State != CandleStates.Finished) return;
var bullsPower = candle.HighPrice - emaVal;
var bearsPower = candle.LowPrice - emaVal;
var avgPower = (bullsPower + bearsPower) / 2m;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevPower = avgPower; return; }
if (_prevPower == null) { _prevPower = avgPower; return; }
if (_prevPower.Value < 0m && avgPower >= 0m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (_prevPower.Value > 0m && avgPower <= 0m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
_prevPower = avgPower;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bulls_bears_power_average_strategy(Strategy):
"""
Bulls Bears Power Average strategy. Uses EMA with bulls/bears power crossover.
"""
def __init__(self):
super(bulls_bears_power_average_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ema_period = self.Param("EmaPeriod", 13) \
.SetDisplay("EMA Period", "EMA lookback for power calc", "Indicators")
self._prev_power = None
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def ema_period(self):
return self._ema_period.Value
@ema_period.setter
def ema_period(self, value):
self._ema_period.Value = value
def OnReseted(self):
super(bulls_bears_power_average_strategy, self).OnReseted()
self._prev_power = None
def OnStarted2(self, time):
super(bulls_bears_power_average_strategy, self).OnStarted2(time)
self._prev_power = None
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(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, ema_val):
if candle.State != CandleStates.Finished:
return
bulls_power = candle.HighPrice - ema_val
bears_power = candle.LowPrice - ema_val
avg_power = (bulls_power + bears_power) / 2.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_power = avg_power
return
if self._prev_power is None:
self._prev_power = avg_power
return
if self._prev_power < 0.0 and avg_power >= 0.0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_power > 0.0 and avg_power <= 0.0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_power = avg_power
def CreateClone(self):
return bulls_bears_power_average_strategy()