La estrategia opera en torno a una media móvil simple (SMA) con un desplazamiento de suavizado adicional. Intenta explotar las desviaciones del precio respecto a la media móvil entrando en posiciones cuando el precio de cierre cruza una distancia de desplazamiento desde la media.
Cómo funciona
Calcular una SMA del tipo de vela elegido.
Si no hay posición abierta:
Entrar en posición corta cuando el precio de cierre esté por debajo de SMA + Smoothing.
Entrar en posición larga cuando el precio de cierre esté por encima de SMA - Smoothing.
Para una posición corta abierta:
Cerrar la posición cuando el precio de cierre suba por encima de SMA + Smoothing.
Para una posición larga abierta:
Cerrar la posición cuando el precio de cierre caiga por debajo de SMA - Smoothing.
La estrategia utiliza órdenes de mercado y trabaja únicamente con velas finalizadas.
Parámetros
MA Period – periodo de retrospección para la SMA.
Smoothing – desplazamiento de precio añadido o restado de la SMA al generar señales.
Candle Type – marco temporal de las velas utilizadas para los cálculos.
Notas
Esta conversión está basada en el script MQL4 original smoothingaverage.mq4.
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>
/// Moving average strategy with smoothing offset.
/// </summary>
public class SmoothingAverageStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _smoothing;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public decimal Smoothing
{
get => _smoothing.Value;
set => _smoothing.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public SmoothingAverageStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 200)
.SetDisplay("MA Period", "Moving average period", "MA")
;
_smoothing = Param(nameof(Smoothing), 1400m)
.SetDisplay("Smoothing", "Price offset from moving average", "General")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 36)
.SetDisplay("Cooldown Bars", "Bars to wait between new signals", "General")
;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// create moving average indicator
var sma = new SimpleMovingAverage { Length = MaPeriod };
// subscribe to candles
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var price = candle.ClosePrice;
var offset = (Security.PriceStep ?? 1m) * Smoothing;
if (Position == 0)
{
if (price >= maValue + offset)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (price <= maValue - offset)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
else if (Position < 0 && price <= maValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && price >= maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
}
}
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, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class smoothing_average_strategy(Strategy):
def __init__(self):
super(smoothing_average_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 200) \
.SetDisplay("MA Period", "Moving average period", "MA")
self._smoothing = self.Param("Smoothing", 1400.0) \
.SetDisplay("Smoothing", "Price offset from moving average", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 36) \
.SetDisplay("Cooldown Bars", "Bars to wait between new signals", "General")
self._cooldown = 0
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
@property
def Smoothing(self):
return self._smoothing.Value
@Smoothing.setter
def Smoothing(self, value):
self._smoothing.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
def OnStarted2(self, time):
super(smoothing_average_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(sma, self.ProcessCandle) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
price = float(candle.ClosePrice)
ma_val = float(ma_value)
step_raw = self.Security.PriceStep
step = float(step_raw) if step_raw is not None else 1.0
offset = step * float(self.Smoothing)
if self.Position == 0:
if price >= ma_val + offset:
self.SellMarket()
self._cooldown = self.CooldownBars
elif price <= ma_val - offset:
self.BuyMarket()
self._cooldown = self.CooldownBars
elif self.Position < 0 and price <= ma_val:
self.BuyMarket()
self._cooldown = self.CooldownBars
elif self.Position > 0 and price >= ma_val:
self.SellMarket()
self._cooldown = self.CooldownBars
def OnReseted(self):
super(smoothing_average_strategy, self).OnReseted()
self._cooldown = 0
def CreateClone(self):
return smoothing_average_strategy()