Стратегия представляет собой контртрендовую систему, основанную на наклоне простой скользящей средней (SMA). Она портирована из советника MetaTrader 5 «Multik_SMA_Exp».
Стратегия анализирует последние три значения SMA. Если SMA падает на двух последних завершённых отрезках, открывается длинная позиция. Если SMA растёт на двух отрезках, открывается короткая позиция. Позиции закрываются при изменении знака наклона SMA.
Параметры
MA Period – период простой скользящей средней. По умолчанию: 50.
Candle Type – тип свечей, используемых в расчётах. По умолчанию: таймфрейм 1 минута.
Правила торговли
На каждой завершённой свече рассчитывается SMA.
Определяются наклоны:
dsma1 = SMA[n-1] - SMA[n-2]
dsma2 = SMA[n-2] - SMA[n-3]
Вход:
Если dsma1 < 0 и dsma2 < 0 и нет длинной позиции, совершается покупка.
Если dsma1 > 0 и dsma2 > 0 и нет короткой позиции, совершается продажа.
Выход:
Если есть длинная позиция и dsma1 > 0, позиция закрывается.
Если есть короткая позиция и dsma1 < 0, позиция закрывается.
Объём новых заявок равен параметру Volume стратегии плюс абсолютное значение текущей позиции, что позволяет полностью переворачивать позицию при необходимости.
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()