Esta estrategia implementa un enfoque contrario basado en la pendiente de una media móvil simple (SMA). Fue portada del asesor experto de MetaTrader 5 "Multik_SMA_Exp".
La estrategia monitorea los últimos tres valores de la SMA. Si la SMA ha estado cayendo durante los dos segmentos completados más recientes, la estrategia entra en una posición larga. Si la SMA ha estado subiendo durante los dos segmentos, abre una posición corta. Las posiciones se cierran cuando la pendiente de la SMA se revierte.
Parámetros
MA Period – longitud de la media móvil simple. Predeterminado: 50.
Candle Type – tipo de velas usadas para los cálculos. Predeterminado: marco temporal de 1 minuto.
Reglas de trading
En cada vela cerrada, calcular la SMA.
Determinar las pendientes:
dsma1 = SMA[n-1] - SMA[n-2]
dsma2 = SMA[n-2] - SMA[n-3]
Entrada:
Si dsma1 < 0 y dsma2 < 0 y no hay posición larga, comprar.
Si dsma1 > 0 y dsma2 > 0 y no hay posición corta, vender.
Salida:
Si se mantiene una posición larga y dsma1 > 0, cerrar la posición larga.
Si se mantiene una posición corta y dsma1 < 0, cerrar la posición corta.
El volumen de las nuevas órdenes usa el Volume de la estrategia más el valor absoluto de la posición actual para revertirla completamente cuando sea necesario.
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>
/// Contrarian strategy based on moving average slope.
/// Buys when SMA decreases for two consecutive periods, sells when it increases.
/// </summary>
public class MultikSmaExpStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<DataType> _candleType;
private decimal? _ma0;
private decimal? _ma1;
private decimal? _ma2;
public int Period { get => _period.Value; set => _period.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MultikSmaExpStrategy()
{
_period = Param(nameof(Period), 50)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Length of the moving average", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma0 = _ma1 = _ma2 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma0 = _ma1 = _ma2 = null;
var sma = new ExponentialMovingAverage { Length = Period };
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 smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_ma2 = _ma1;
_ma1 = _ma0;
_ma0 = smaValue;
if (_ma2 is null || _ma1 is null)
return;
var dsma1 = _ma0.Value - _ma1.Value;
var dsma2 = _ma1.Value - _ma2.Value;
// Two consecutive decreases -> contrarian buy
if (dsma2 < 0 && dsma1 < 0 && Position <= 0)
BuyMarket();
// Two consecutive increases -> contrarian sell
else if (dsma2 > 0 && dsma1 > 0 && Position >= 0)
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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class multik_sma_exp_strategy(Strategy):
def __init__(self):
super(multik_sma_exp_strategy, self).__init__()
self._period = self.Param("Period", 50) \
.SetDisplay("MA Period", "Length of the moving average", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._ma0 = None
self._ma1 = None
self._ma2 = None
@property
def period(self):
return self._period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(multik_sma_exp_strategy, self).OnReseted()
self._ma0 = None
self._ma1 = None
self._ma2 = None
def OnStarted2(self, time):
super(multik_sma_exp_strategy, self).OnStarted2(time)
self._ma0 = None
self._ma1 = None
self._ma2 = None
sma = ExponentialMovingAverage()
sma.Length = self.period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
sma_value = float(sma_value)
self._ma2 = self._ma1
self._ma1 = self._ma0
self._ma0 = sma_value
if self._ma2 is None or self._ma1 is None:
return
dsma1 = self._ma0 - self._ma1
dsma2 = self._ma1 - self._ma2
if dsma2 < 0 and dsma1 < 0 and self.Position <= 0:
self.BuyMarket()
elif dsma2 > 0 and dsma1 > 0 and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return multik_sma_exp_strategy()