La estrategia usa el indicador Modified Moving Average (AMMA) para capturar cambios de tendencia a corto plazo. Analiza la dirección de la pendiente de AMMA en las velas recientes y abre una posición en la dirección de la tendencia emergente mientras cierra la opuesta.
Cómo funciona
Se calcula una ModifiedMovingAverage con un período configurable en el marco temporal seleccionado.
En cada vela terminada, la estrategia compara los últimos tres valores de AMMA.
Si los valores del indicador forman una secuencia ascendente y el valor más reciente es mayor que el anterior, se abre una posición larga. Se cierra cualquier posición corta.
Si los valores del indicador forman una secuencia descendente y el valor más reciente es menor que el anterior, se abre una posición corta. Se cierra cualquier posición larga.
Parámetros
CandleType – marco temporal de las velas utilizadas para los cálculos.
MaPeriod – período de la media móvil modificada.
AllowLongEntry – habilitar la apertura de posiciones largas.
AllowShortEntry – habilitar la apertura de posiciones cortas.
AllowLongExit – habilitar el cierre de posiciones largas.
AllowShortExit – habilitar el cierre de posiciones cortas.
Notas
La estrategia opera únicamente en velas completadas y se apoya en los métodos incorporados BuyMarket y SellMarket para la ejecución de órdenes. Las funciones de gestión de riesgo pueden añadirse externamente usando las propiedades estándar de Strategy.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// AMMA Trend strategy using Modified Moving Average direction changes.
/// Buys when MMA turns up, sells when MMA turns down.
/// </summary>
public class AmmaTrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private decimal? _mma0;
private decimal? _mma1;
private decimal? _mma2;
private decimal? _mma3;
private readonly StrategyParam<bool> _allowLongEntry, _allowShortEntry, _allowLongExit, _allowShortExit;
private Order _pending;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public bool AllowLongEntry { get => _allowLongEntry.Value; set => _allowLongEntry.Value = value; }
public bool AllowShortEntry { get => _allowShortEntry.Value; set => _allowShortEntry.Value = value; }
public bool AllowLongExit { get => _allowLongExit.Value; set => _allowLongExit.Value = value; }
public bool AllowShortExit { get => _allowShortExit.Value; set => _allowShortExit.Value = value; }
public AmmaTrendStrategy()
{
_allowLongEntry = Param(nameof(AllowLongEntry), true);
_allowShortEntry = Param(nameof(AllowShortEntry), true);
_allowLongExit = Param(nameof(AllowLongExit), true);
_allowShortExit = Param(nameof(AllowShortExit), true);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use for analysis", "General");
_maPeriod = Param(nameof(MaPeriod), 25)
.SetGreaterThanZero()
.SetDisplay("AMMA Period", "Period of the modified moving average", "Indicator");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_mma0 = _mma1 = _mma2 = _mma3 = null;
_pending = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pending = null;
var mma = new SmoothedMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(mma, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, mma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal mmaValue)
{
if (candle.State != CandleStates.Finished)
return;
_mma3 = _mma2;
_mma2 = _mma1;
_mma1 = _mma0;
_mma0 = mmaValue;
if (_mma1 is null || _mma2 is null || _mma3 is null)
return;
var rising = _mma2 < _mma3 && _mma1 > _mma2 && _mma0 > _mma1;
var falling = _mma2 > _mma3 && _mma1 < _mma2 && _mma0 < _mma1;
if (!IsFormedAndOnlineAndAllowTrading() || (_pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed))) return;
if (rising)
{
if (Position < 0m && AllowShortExit) _pending = BuyMarket(-Position + (AllowLongEntry ? Volume : 0m));
else if (Position == 0m && AllowLongEntry) _pending = BuyMarket(Volume);
}
else if (falling)
{
if (Position > 0m && AllowLongExit) _pending = SellMarket(Position + (AllowShortEntry ? Volume : 0m));
else if (Position == 0m && AllowShortEntry) _pending = SellMarket(Volume);
}
}
}
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, OrderStates
from StockSharp.Algo.Indicators import SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class amma_trend_strategy(Strategy):
def __init__(self):
super(amma_trend_strategy, self).__init__()
self._allow_long_entry = self.Param("AllowLongEntry", True)
self._allow_short_entry = self.Param("AllowShortEntry", True)
self._allow_long_exit = self.Param("AllowLongExit", True)
self._allow_short_exit = self.Param("AllowShortExit", True)
self._pending = None
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use for analysis", "General")
self._ma_period = self.Param("MaPeriod", 25) \
.SetDisplay("AMMA Period", "Period of the modified moving average", "Indicator")
self._mma0 = None
self._mma1 = None
self._mma2 = None
self._mma3 = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
def OnStarted2(self, time):
super(amma_trend_strategy, self).OnStarted2(time)
self._pending = None
self._mma0 = None
self._mma1 = None
self._mma2 = None
self._mma3 = None
mma = SmoothedMovingAverage()
mma.Length = self.MaPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(mma, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, mma_val):
if candle.State != CandleStates.Finished:
return
mma_f = float(mma_val)
self._mma3 = self._mma2
self._mma2 = self._mma1
self._mma1 = self._mma0
self._mma0 = mma_f
if self._mma1 is None or self._mma2 is None or self._mma3 is None:
return
rising = self._mma2 < self._mma3 and self._mma1 > self._mma2 and self._mma0 > self._mma1
falling = self._mma2 > self._mma3 and self._mma1 < self._mma2 and self._mma0 < self._mma1
if not self.IsFormedAndOnlineAndAllowTrading() or (self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed)):
return
if rising:
if self.Position < 0 and self._allow_short_exit.Value:
self._pending = self.BuyMarket(-self.Position + (self.Volume if self._allow_long_entry.Value else 0))
elif self.Position == 0 and self._allow_long_entry.Value:
self._pending = self.BuyMarket(self.Volume)
elif falling:
if self.Position > 0 and self._allow_long_exit.Value:
self._pending = self.SellMarket(self.Position + (self.Volume if self._allow_short_entry.Value else 0))
elif self.Position == 0 and self._allow_short_entry.Value:
self._pending = self.SellMarket(self.Volume)
def OnReseted(self):
super(amma_trend_strategy, self).OnReseted()
self._pending = None
self._mma0 = None
self._mma1 = None
self._mma2 = None
self._mma3 = None
def CreateClone(self):
return amma_trend_strategy()