Стратегия прорыва на основе импульса
Эта система прорыва отслеживает резкие всплески импульса относительно его исторического среднего. Когда значения импульса значительно превышают среднее, цена может начать быстрое направленное движение.
Тестирование показывает среднегодичную доходность около 82%. Стратегию лучше запускать на фондовом рынке.
Стратегия покупает, когда импульс поднимается выше среднего плюс Multiplier, умноженный на стандартное отклонение. Шорт открывается, когда импульс опускается ниже среднего минус тот же множитель. Позиции закрываются, когда импульс возвращается к своему среднему.
Трейдерам, предпочитающим быстрые движения, могут понравиться четкие правила для захвата всплесков силы. Стоп‑лосс в процентах от цены защищает от ложных прорывов.
Подробности
- Условия входа:
- Лонг: Momentum > Avg + Multiplier * StdDev
- Шорт: Momentum < Avg - Multiplier * StdDev
- Длинные/короткие: обе стороны.
- Условия выхода:
- Лонг: выход при Momentum < Avg
- Шорт: выход при Momentum > Avg
- Стопы: да, процентный стоп‑лосс.
- Значения по умолчанию:
MomentumPeriod= 14AveragePeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Breakout
- Направление: оба
- Индикаторы: Momentum
- Стопы: да
- Сложность: средняя
- Таймфрейм: внутридневной
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: средний
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()