Anchored Momentum
Стратегия Anchored Momentum рассчитывает соотношение EMA и SMA закрытий свечей. При превышении верхнего порога открывается длинная позиция, при падении ниже нижнего порога — короткая. Противоположный сигнал закрывает открытую позицию.
Подробности
- Условия входа: пересечение уровня
UpLevelвверх для покупки,DownLevelвниз для продажи. - Длинные/короткие: обе стороны.
- Условия выхода: противоположный сигнал.
- Стопы: нет.
- Значения по умолчанию:
SmaPeriod= 8EmaPeriod= 6UpLevel= 0.025DownLevel= -0.025CandleType= 4 часа
- Фильтры:
- Категория: Тренд
- Направление: Оба
- Индикаторы: SMA, EMA
- Стопы: Нет
- Сложность: Базовая
- Таймфрейм: 4 часа
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Strategy based on the Anchored Momentum indicator.
/// Calculates the ratio between EMA and SMA to detect trend strength.
/// Opens long positions when momentum rises above the upper level and
/// opens short positions when momentum falls below the lower level.
/// </summary>
public class AnchoredMomentumStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _upLevel;
private readonly StrategyParam<decimal> _downLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _buyEnabled;
private readonly StrategyParam<bool> _sellEnabled;
private decimal _previousMomentum;
private bool _isFirstValue;
/// <summary>
/// Period for the simple moving average.
/// </summary>
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
/// <summary>
/// Period for the exponential moving average.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Upper threshold for the momentum.
/// </summary>
public decimal UpLevel
{
get => _upLevel.Value;
set => _upLevel.Value = value;
}
/// <summary>
/// Lower threshold for the momentum.
/// </summary>
public decimal DownLevel
{
get => _downLevel.Value;
set => _downLevel.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyEnabled
{
get => _buyEnabled.Value;
set => _buyEnabled.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellEnabled
{
get => _sellEnabled.Value;
set => _sellEnabled.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public AnchoredMomentumStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "Period of the simple moving average", "Indicator");
_emaPeriod = Param(nameof(EmaPeriod), 6)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Period of the exponential moving average", "Indicator");
_upLevel = Param(nameof(UpLevel), 0.025m)
.SetDisplay("Upper Level", "Upper threshold for momentum", "Indicator");
_downLevel = Param(nameof(DownLevel), -0.025m)
.SetDisplay("Lower Level", "Lower threshold for momentum", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used by the strategy", "General");
_buyEnabled = Param(nameof(BuyEnabled), true)
.SetDisplay("Enable Buy", "Allow opening long positions", "Trading");
_sellEnabled = Param(nameof(SellEnabled), true)
.SetDisplay("Enable Sell", "Allow opening short positions", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousMomentum = 0m;
_isFirstValue = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var momentum = smaValue == 0m ? 0m : 100m * (emaValue / smaValue - 1m);
if (_isFirstValue)
{
_previousMomentum = momentum;
_isFirstValue = false;
return;
}
if (_previousMomentum <= UpLevel && momentum > UpLevel)
{
if (SellEnabled && Position < 0)
BuyMarket(Math.Abs(Position));
if (BuyEnabled && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
else if (_previousMomentum >= DownLevel && momentum < DownLevel)
{
if (BuyEnabled && Position > 0)
SellMarket(Math.Abs(Position));
if (SellEnabled && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
_previousMomentum = momentum;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class anchored_momentum_strategy(Strategy):
"""
Strategy based on the Anchored Momentum indicator.
Calculates the ratio between EMA and SMA to detect trend strength.
Opens long when momentum rises above upper level, short when below lower level.
"""
def __init__(self):
super(anchored_momentum_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 8) \
.SetGreaterThanZero() \
.SetDisplay("SMA Period", "Period of the simple moving average", "Indicator")
self._ema_period = self.Param("EmaPeriod", 6) \
.SetGreaterThanZero() \
.SetDisplay("EMA Period", "Period of the exponential moving average", "Indicator")
self._up_level = self.Param("UpLevel", 0.025) \
.SetDisplay("Upper Level", "Upper threshold for momentum", "Indicator")
self._down_level = self.Param("DownLevel", -0.025) \
.SetDisplay("Lower Level", "Lower threshold for momentum", "Indicator")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles used by the strategy", "General")
self._buy_enabled = self.Param("BuyEnabled", True) \
.SetDisplay("Enable Buy", "Allow opening long positions", "Trading")
self._sell_enabled = self.Param("SellEnabled", True) \
.SetDisplay("Enable Sell", "Allow opening short positions", "Trading")
self._previous_momentum = 0.0
self._is_first_value = True
@property
def SmaPeriod(self): return self._sma_period.Value
@SmaPeriod.setter
def SmaPeriod(self, v): self._sma_period.Value = v
@property
def EmaPeriod(self): return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, v): self._ema_period.Value = v
@property
def UpLevel(self): return self._up_level.Value
@UpLevel.setter
def UpLevel(self, v): self._up_level.Value = v
@property
def DownLevel(self): return self._down_level.Value
@DownLevel.setter
def DownLevel(self, v): self._down_level.Value = v
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
@property
def BuyEnabled(self): return self._buy_enabled.Value
@BuyEnabled.setter
def BuyEnabled(self, v): self._buy_enabled.Value = v
@property
def SellEnabled(self): return self._sell_enabled.Value
@SellEnabled.setter
def SellEnabled(self, v): self._sell_enabled.Value = v
def OnReseted(self):
super(anchored_momentum_strategy, self).OnReseted()
self._previous_momentum = 0.0
self._is_first_value = True
def OnStarted2(self, time):
super(anchored_momentum_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.SmaPeriod
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, ema, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, sma_value, ema_value):
if candle.State != CandleStates.Finished:
return
momentum = 0.0 if sma_value == 0 else 100.0 * (ema_value / sma_value - 1.0)
if self._is_first_value:
self._previous_momentum = momentum
self._is_first_value = False
return
if self._previous_momentum <= self.UpLevel and momentum > self.UpLevel:
if self.SellEnabled and self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
if self.BuyEnabled and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif self._previous_momentum >= self.DownLevel and momentum < self.DownLevel:
if self.BuyEnabled and self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
if self.SellEnabled and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
self._previous_momentum = momentum
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return anchored_momentum_strategy()