动量突破策略
该系统寻找动量相对其历史平均值的突然增加。当动量读数大幅高于平均水平时,价格可能开始快速单边运行。
测试表明年均收益约为 82%,该策略在股票市场表现最佳。
动量高于均值加 Multiplier 倍标准差时买入;低于均值减同倍数时做空。动量回到均值附近便平仓。
此策略适合喜好追逐快速行情的交易者,止损按价格百分比设置以防突破失败。
详细信息
- 入场条件:
- 做多: 动量 > Avg + Multiplier * StdDev
- 做空: 动量 < Avg - Multiplier * StdDev
- 多空方向: 双向
- 退出条件:
- 做多: Exit when 动量 < Avg
- 做空: Exit when 动量 > Avg
- 止损: 是
- 默认值:
动量Period= 14AveragePeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- 筛选条件:
- 类别: 突破
- 方向: 双向
- 指标: 动量
- 止损: 是
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Momentum Breakout Strategy (245).
/// Enter when momentum breaks out above/below its average by a certain multiple of standard deviation.
/// Exit when momentum returns to its average.
/// </summary>
public class MomentumBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<int> _averagePeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private Momentum _momentum;
private SimpleMovingAverage _momentumAverage;
private StandardDeviation _momentumStdDev;
private decimal? _currentMomentum;
private decimal? _momentumAvgValue;
private decimal? _momentumStdDevValue;
/// <summary>
/// Momentum period.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Period for momentum average calculation.
/// </summary>
public int AveragePeriod
{
get => _averagePeriod.Value;
set => _averagePeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for entry.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="MomentumBreakoutStrategy"/>.
/// </summary>
public MomentumBreakoutStrategy()
{
_momentumPeriod = Param(nameof(MomentumPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Period for momentum calculation", "Strategy Parameters")
.SetOptimize(10, 20, 2);
_averagePeriod = Param(nameof(AveragePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Average Period", "Period for momentum average calculation", "Strategy Parameters")
.SetOptimize(10, 30, 5);
_multiplier = Param(nameof(Multiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("StdDev Multiplier", "Standard deviation multiplier for entry", "Strategy Parameters")
.SetOptimize(1.0m, 3.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "Strategy Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentMomentum = default;
_momentumAvgValue = default;
_momentumStdDevValue = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_momentum = new Momentum { Length = MomentumPeriod };
_momentumAverage = new SMA { Length = AveragePeriod };
_momentumStdDev = new StandardDeviation { Length = AveragePeriod };
// Create candle subscription
var subscription = SubscribeCandles(CandleType);
// Create processing chain
subscription
.Bind(_momentum, ProcessMomentum)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _momentum);
DrawOwnTrades(area);
}
// Enable position protection
StartProtection(
takeProfit: new Unit(5, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent)
);
}
private void ProcessMomentum(ICandleMessage candle, decimal momentumValue)
{
if (candle.State != CandleStates.Finished)
return;
// Store the current momentum value
_currentMomentum = momentumValue;
// Process momentum through average and standard deviation indicators
var avgIndicatorValue = _momentumAverage.Process(new DecimalIndicatorValue(_momentumAverage, momentumValue, candle.ServerTime) { IsFinal = true });
var stdDevIndicatorValue = _momentumStdDev.Process(new DecimalIndicatorValue(_momentumStdDev, momentumValue, candle.ServerTime) { IsFinal = true });
_momentumAvgValue = avgIndicatorValue.ToDecimal();
_momentumStdDevValue = stdDevIndicatorValue.ToDecimal();
if (!_momentumAverage.IsFormed || !_momentumStdDev.IsFormed)
return;
// Ensure we have all needed values
if (!_currentMomentum.HasValue || !_momentumAvgValue.HasValue || !_momentumStdDevValue.HasValue)
return;
// Calculate bands
var upperBand = _momentumAvgValue.Value + Multiplier * _momentumStdDevValue.Value;
var lowerBand = _momentumAvgValue.Value - Multiplier * _momentumStdDevValue.Value;
LogInfo($"Momentum: {_currentMomentum}, Avg: {_momentumAvgValue}, Upper: {upperBand}, Lower: {lowerBand}");
// Entry logic - BREAKOUT (not mean reversion)
if (Position == 0)
{
// Long Entry: Momentum breaks above upper band (strong upward momentum)
if (_currentMomentum.Value > upperBand)
{
BuyMarket();
}
// Short Entry: Momentum breaks below lower band (strong downward momentum)
else if (_currentMomentum.Value < lowerBand)
{
SellMarket();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import Momentum, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class momentum_breakout_strategy(Strategy):
"""
Momentum Breakout Strategy (245).
Enter when momentum breaks out above/below its average by a certain multiple of standard deviation.
Exit when momentum returns to its average.
"""
def __init__(self):
super(momentum_breakout_strategy, self).__init__()
# Initialize strategy parameters
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Momentum Period", "Period for momentum calculation", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 20, 2)
self._average_period = self.Param("AveragePeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Average Period", "Period for momentum average calculation", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 30, 5)
self._multiplier = self.Param("Multiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("StdDev Multiplier", "Standard deviation multiplier for entry", "Strategy Parameters") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "Strategy Parameters")
# Indicators
self._momentum = None
self._momentum_average = None
self._momentum_stddev = None
self._current_momentum = None
self._momentum_avg_value = None
self._momentum_stddev_value = None
@property
def momentum_period(self):
"""Momentum period."""
return self._momentum_period.Value
@momentum_period.setter
def momentum_period(self, value):
self._momentum_period.Value = value
@property
def average_period(self):
"""Period for momentum average calculation."""
return self._average_period.Value
@average_period.setter
def average_period(self, value):
self._average_period.Value = value
@property
def multiplier(self):
"""Standard deviation multiplier for entry."""
return self._multiplier.Value
@multiplier.setter
def multiplier(self, value):
self._multiplier.Value = value
@property
def candle_type(self):
"""Type of candles to use."""
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(momentum_breakout_strategy, self).OnReseted()
self._current_momentum = None
self._momentum_avg_value = None
self._momentum_stddev_value = None
def OnStarted2(self, time):
"""Called when the strategy starts."""
super(momentum_breakout_strategy, self).OnStarted2(time)
# Create indicators
self._momentum = Momentum()
self._momentum.Length = self.momentum_period
self._momentum_average = SimpleMovingAverage()
self._momentum_average.Length = self.average_period
self._momentum_stddev = StandardDeviation()
self._momentum_stddev.Length = self.average_period
# Create candle subscription
subscription = self.SubscribeCandles(self.candle_type)
# Create processing chain
subscription.Bind(self._momentum, self.ProcessMomentum).Start()
# Setup chart visualization if available
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._momentum)
self.DrawOwnTrades(area)
# Enable position protection
self.StartProtection(
takeProfit=Unit(5, UnitTypes.Percent),
stopLoss=Unit(2, UnitTypes.Percent)
)
def ProcessMomentum(self, candle, momentum_value):
if candle.State != CandleStates.Finished:
return
# Store the current momentum value
self._current_momentum = momentum_value
# Process momentum through average and standard deviation indicators
avg_val = process_float(self._momentum_average, momentum_value, candle.ServerTime, candle.State == CandleStates.Finished)
std_val = process_float(self._momentum_stddev, momentum_value, candle.ServerTime, candle.State == CandleStates.Finished)
self._momentum_avg_value = float(avg_val)
self._momentum_stddev_value = float(std_val)
if not self._momentum_average.IsFormed or not self._momentum_stddev.IsFormed:
return
# Ensure we have all needed values
if self._current_momentum is None or self._momentum_avg_value is None or self._momentum_stddev_value is None:
return
# Calculate bands
upper_band = self._momentum_avg_value + self.multiplier * self._momentum_stddev_value
lower_band = self._momentum_avg_value - self.multiplier * self._momentum_stddev_value
self.LogInfo("Momentum: {0}, Avg: {1}, Upper: {2}, Lower: {3}".format(
self._current_momentum, self._momentum_avg_value, upper_band, lower_band))
# Entry logic - BREAKOUT only when flat (no exit logic in CS)
if self.Position == 0:
if self._current_momentum > upper_band:
self.BuyMarket()
elif self._current_momentum < lower_band:
self.SellMarket()
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return momentum_breakout_strategy()