Стратегия Dig Variation
Стратегия основана на примере MQL5 DigVariation. Индикатор аппроксимирован простым скользящим средним (SMA). Сделки открываются, когда направление SMA меняется.
Логика
- Рассчитывается SMA по поступающим свечам.
- Если предыдущие значения SMA растут и текущее значение продолжает движение вверх, открывается длинная позиция.
- Если предыдущие значения SMA падают и текущее значение продолжает движение вниз, открывается короткая позиция.
- При смене тренда имеющиеся позиции закрываются.
Параметры
- Period – период расчёта SMA.
- BuyOpen – разрешить входы в лонг.
- SellOpen – разрешить входы в шорт.
- BuyClose – разрешить закрытие длинных позиций.
- SellClose – разрешить закрытие коротких позиций.
- StopLoss – значение для защиты от убытков (используется в
StartProtection). - TakeProfit – значение для фиксации прибыли (используется в
StartProtection).
Примечания
Это упрощённая конверсия: вместо оригинального индикатора DigVariation используется стандартный SMA.
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>
/// Simple moving average based strategy inspired by DigVariation indicator.
/// Opens a position when the moving average trend reverses.
/// </summary>
public class DigVariationStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<bool> _buyOpen;
private readonly StrategyParam<bool> _sellOpen;
private readonly StrategyParam<bool> _buyClose;
private readonly StrategyParam<bool> _sellClose;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private decimal _prev;
private decimal _prevPrev;
private bool _initialized;
private int _cooldown;
/// <summary>
/// Initializes a new instance of the <see cref="DigVariationStrategy"/> class.
/// </summary>
public DigVariationStrategy()
{
_period = this.Param("Period", 20).SetDisplay("Period", "Period", "General");
_buyOpen = this.Param("BuyOpen", true).SetDisplay("Open Long", "Open Long", "General");
_sellOpen = this.Param("SellOpen", true).SetDisplay("Open Short", "Open Short", "General");
_buyClose = this.Param("BuyClose", true).SetDisplay("Close Long", "Close Long", "General");
_sellClose = this.Param("SellClose", true).SetDisplay("Close Short", "Close Short", "General");
_stopLoss = this.Param("StopLoss", 1000m).SetDisplay("Stop Loss", "Stop Loss", "General");
_takeProfit = this.Param("TakeProfit", 2000m).SetDisplay("Take Profit", "Take Profit", "General");
_candleType2 = this.Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle", "General");
_cooldownPeriod = this.Param(nameof(CooldownPeriod), 200).SetDisplay("Cooldown", "Cooldown between trades in candles", "General");
}
/// <summary>
/// Moving average period.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Allow long entries.
/// </summary>
public bool BuyOpen
{
get => _buyOpen.Value;
set => _buyOpen.Value = value;
}
/// <summary>
/// Allow short entries.
/// </summary>
public bool SellOpen
{
get => _sellOpen.Value;
set => _sellOpen.Value = value;
}
/// <summary>
/// Allow long exits.
/// </summary>
public bool BuyClose
{
get => _buyClose.Value;
set => _buyClose.Value = value;
}
/// <summary>
/// Allow short exits.
/// </summary>
public bool SellClose
{
get => _sellClose.Value;
set => _sellClose.Value = value;
}
/// <summary>
/// Stop loss value.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit value.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
private readonly StrategyParam<DataType> _candleType2;
private readonly StrategyParam<int> _cooldownPeriod;
public DataType CandleType { get => _candleType2.Value; set => _candleType2.Value = value; }
/// <summary>
/// Cooldown period between trades in candles.
/// </summary>
public int CooldownPeriod { get => _cooldownPeriod.Value; set => _cooldownPeriod.Value = value; }
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prev = default;
_prevPrev = default;
_initialized = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(new Unit(StopLoss, UnitTypes.Absolute), new Unit(TakeProfit, UnitTypes.Absolute));
var ema = new ExponentialMovingAverage { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_prev = smaValue;
_prevPrev = smaValue;
_initialized = true;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevPrev = _prev;
_prev = smaValue;
return;
}
var wasRising = _prev > _prevPrev;
var wasFalling = _prev < _prevPrev;
if (wasRising)
{
if (SellClose && Position < 0)
{
BuyMarket();
_cooldown = CooldownPeriod;
}
else if (BuyOpen && Position <= 0 && smaValue > _prev)
{
BuyMarket();
_cooldown = CooldownPeriod;
}
}
if (wasFalling)
{
if (BuyClose && Position > 0)
{
SellMarket();
_cooldown = CooldownPeriod;
}
else if (SellOpen && Position >= 0 && smaValue < _prev)
{
SellMarket();
_cooldown = CooldownPeriod;
}
}
_prevPrev = _prev;
_prev = smaValue;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class dig_variation_strategy(Strategy):
def __init__(self):
super(dig_variation_strategy, self).__init__()
self._period = self.Param("Period", 20) \
.SetDisplay("Period", "Period", "General")
self._buy_open = self.Param("BuyOpen", True) \
.SetDisplay("Open Long", "Open Long", "General")
self._sell_open = self.Param("SellOpen", True) \
.SetDisplay("Open Short", "Open Short", "General")
self._buy_close = self.Param("BuyClose", True) \
.SetDisplay("Close Long", "Close Long", "General")
self._sell_close = self.Param("SellClose", True) \
.SetDisplay("Close Short", "Close Short", "General")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop Loss", "General")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take Profit", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle", "General")
self._cooldown_period = self.Param("CooldownPeriod", 200) \
.SetDisplay("Cooldown", "Cooldown between trades in candles", "General")
self._prev = 0.0
self._prev_prev = 0.0
self._initialized = False
self._cooldown = 0
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def BuyOpen(self):
return self._buy_open.Value
@BuyOpen.setter
def BuyOpen(self, value):
self._buy_open.Value = value
@property
def SellOpen(self):
return self._sell_open.Value
@SellOpen.setter
def SellOpen(self, value):
self._sell_open.Value = value
@property
def BuyClose(self):
return self._buy_close.Value
@BuyClose.setter
def BuyClose(self, value):
self._buy_close.Value = value
@property
def SellClose(self):
return self._sell_close.Value
@SellClose.setter
def SellClose(self, value):
self._sell_close.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def CooldownPeriod(self):
return self._cooldown_period.Value
@CooldownPeriod.setter
def CooldownPeriod(self, value):
self._cooldown_period.Value = value
def OnStarted2(self, time):
super(dig_variation_strategy, self).OnStarted2(time)
self.StartProtection(
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute),
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute)
)
ema = ExponentialMovingAverage()
ema.Length = self.Period
self.SubscribeCandles(self.CandleType) \
.Bind(ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
val = float(ema_value)
if not self._initialized:
self._prev = val
self._prev_prev = val
self._initialized = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_prev = self._prev
self._prev = val
return
was_rising = self._prev > self._prev_prev
was_falling = self._prev < self._prev_prev
if was_rising:
if self.SellClose and self.Position < 0:
self.BuyMarket()
self._cooldown = self.CooldownPeriod
elif self.BuyOpen and self.Position <= 0 and val > self._prev:
self.BuyMarket()
self._cooldown = self.CooldownPeriod
if was_falling:
if self.BuyClose and self.Position > 0:
self.SellMarket()
self._cooldown = self.CooldownPeriod
elif self.SellOpen and self.Position >= 0 and val < self._prev:
self.SellMarket()
self._cooldown = self.CooldownPeriod
self._prev_prev = self._prev
self._prev = val
def OnReseted(self):
super(dig_variation_strategy, self).OnReseted()
self._prev = 0.0
self._prev_prev = 0.0
self._initialized = False
self._cooldown = 0
def CreateClone(self):
return dig_variation_strategy()