在 GitHub 上查看
MA Break Impulse Buy 策略
概述
本策略按照 StockSharp 高层 API 重现 “M.A break mt4 buy” 智能交易顾问。核心思想是在一段低波动的整固之后捕捉强势的多头突破。交易逻辑依次检查多条指数移动平均线(EMA)、静默期以及触碰突破 EMA 的强势多头蜡烛。策略 只做多。
交易逻辑
- EMA 趋势过滤
- 在上一根完成的 K 线上计算两个 EMA 组合(
shift = 1)。
EMA(FirstFastPeriod) 必须高于 EMA(FirstSlowPeriod)。
EMA(SecondFastPeriod) 必须高于 EMA(SecondSlowPeriod)。
- 突破蜡烛筛选
- 突破蜡烛是上一根完成的 K 线(
shift = 1)。
- 其开盘价需高于
TrendMaPeriod 对应的 EMA。
- 其最低价需触及或跌破
BreakoutMaPeriod EMA。
- 蜡烛必须为阳线(
Close > Open)。
- 蜡烛振幅需位于
CandleMinSize 与 CandleMaxSize 之间(通过 Security.PriceStep 将点值转换为价格单位)。
- 上影线不得超过
UpperWickLimit% 的蜡烛振幅,下影线至少为 LowerWickFloor% 的振幅。
- 静默区与动能要求
- 回看突破蜡烛之前的
QuietBarsCount 根 K 线(shift ≥ 2),记录其中最大高低点差值。
- 该静默区间需大于
QuietBarsMinRange(点值→价格)。
- 突破蜡烛实体(
Close - Open)必须 ≥ ImpulseStrength × 静默区间。
- 持仓管理
- 当以上条件全部满足且当前无仓位时市价买入。
- 使用
StartProtection 根据点值(结合 PriceStep)自动布置止损与止盈。
参数
| 参数 |
默认值 |
说明 |
FirstFastPeriod |
20 |
第一组趋势过滤的快速 EMA 周期。 |
FirstSlowPeriod |
30 |
第一组趋势过滤的慢速 EMA 周期。 |
SecondFastPeriod |
30 |
第二组趋势过滤的快速 EMA 周期。 |
SecondSlowPeriod |
50 |
第二组趋势过滤的慢速 EMA 周期。 |
TrendMaPeriod |
30 |
要求突破蜡烛开盘价高于的 EMA 周期。 |
BreakoutMaPeriod |
20 |
突破蜡烛最低价需要触碰的 EMA 周期。 |
QuietBarsCount |
2 |
统计静默区所需的 K 线数量。 |
QuietBarsMinRange |
0.0 |
静默区间的最小点值。 |
ImpulseStrength |
1.1 |
将静默区间放大后的突破实体要求。 |
UpperWickLimit |
100.0 |
上影线占蜡烛振幅的最大百分比。 |
LowerWickFloor |
0.0 |
下影线占蜡烛振幅的最小百分比。 |
CandleMinSize |
0.0 |
突破蜡烛允许的最小振幅(点值)。 |
CandleMaxSize |
100.0 |
突破蜡烛允许的最大振幅(点值)。 |
VolumeSize |
0.01 |
市价买单的下单量,会按照交易所的 VolumeStep 归一化。 |
StopLossPips |
20.0 |
止损距离(点值,通过 PriceStep 转换)。 |
TakeProfitPips |
20.0 |
止盈距离(点值,通过 PriceStep 转换)。 |
CandleType |
15 分钟 |
订阅的 K 线数据类型。 |
实现说明
- 使用高层
Bind 订阅保持指标与 K 线同步,不需要手工管理指标缓存。
- 仅对完成的 K 线(
CandleStates.Finished)进行计算,避免使用未闭合数据。
- 静默区与蜡烛尺寸过滤会把点值参数通过
Security.PriceStep 转为价格单位;若标的未提供 PriceStep,则退化为 1,与原 MQL 中的 PipValue 逻辑一致。
StartProtection 在 OnStarted 中只调用一次,之后的每笔新仓都会自动带上止损/止盈。
- 蜡烛历史只保留最近
QuietBarsCount + 3 根,以便快速评估静默期与突破蜡烛。
使用建议
- 请确保标的提供
PriceStep、VolumeStep 以及最小/最大下单量,这样点值与手数转换才准确。
- 可根据品种波动性调整 EMA 周期与动能阈值;降低
ImpulseStrength 会更敏感,高值则只接受更强的突破。
- 策略一次只允许一个仓位,若有外部持仓可能阻止新的入场信号。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MA Break Impulse Buy strategy: EMA crossover with volume surge.
/// Buys on EMA cross up with above-average volume, sells on EMA cross down.
/// </summary>
public class MABreakImpulseBuyStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal _prevClose;
private decimal _prevEma;
private decimal _prevVolume;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public MABreakImpulseBuyStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevEma = 0;
_prevVolume = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_prevVolume = 0;
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
var volumeUp = candle.TotalVolume > _prevVolume;
if (_prevClose <= _prevEma && candle.ClosePrice > emaValue && volumeUp && Position <= 0)
BuyMarket();
else if (_prevClose >= _prevEma && candle.ClosePrice < emaValue && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevEma = emaValue;
_prevVolume = candle.TotalVolume;
_hasPrev = true;
}
}
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 ma_break_impulse_buy_strategy(Strategy):
def __init__(self):
super(ma_break_impulse_buy_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ema_period = self.Param("EmaPeriod", 50)
self._prev_close = 0.0
self._prev_ema = 0.0
self._prev_volume = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
def OnReseted(self):
super(ma_break_impulse_buy_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._prev_volume = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ma_break_impulse_buy_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._prev_volume = 0.0
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._process_candle).Start()
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
volume = float(candle.TotalVolume)
if self._has_prev:
volume_up = volume > self._prev_volume
if self._prev_close <= self._prev_ema and close > ema_val and volume_up and self.Position <= 0:
self.BuyMarket()
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
self._prev_volume = volume
self._has_prev = True
def CreateClone(self):
return ma_break_impulse_buy_strategy()