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()