Стратегия ROC2 VG
Реализует в StockSharp эксперт Exp_ROC2_VG из MetaTrader.
Сравниваются две линии темпа изменения цены с настраиваемыми периодами и типами расчёта.
Длинная позиция открывается, когда первая линия пересекает вторую сверху вниз.
Короткая позиция открывается при обратном пересечении. Параметр Invert меняет линии местами.
Детали
- Вход в лонг: предыдущий up > предыдущего down И текущий up <= текущего down.
- Вход в шорт: предыдущий up < предыдущего down И текущий up >= текущего down.
- Выход: сигнал разворота сразу переворачивает позицию рыночными ордерами.
- Таймфрейм: настраиваемый тип свечей, по умолчанию 4 часа.
- Индикаторы: каждая линия может использовать Momentum или варианты ROC:
- Momentum =
цена - предыдущая цена - ROC =
((цена / предыдущая) - 1) * 100 - ROCP =
(цена - предыдущая) / предыдущая - ROCR =
цена / предыдущая - ROCR100 =
(цена / предыдущая) * 100
- Momentum =
- Значения по умолчанию:
RocPeriod1= 8,RocType1= MomentumRocPeriod2= 14,RocType2= MomentumInvert= false
При смене сигнала стратегия разворачивает позицию.
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>
/// Strategy that trades on the crossing of two rate of change lines.
/// </summary>
public class Roc2VgStrategy : Strategy
{
/// <summary>
/// Types of rate of change calculation.
/// </summary>
public enum RocTypes
{
/// <summary>Price - previous price.</summary>
Momentum,
/// <summary>((price / previous) - 1) * 100.</summary>
Roc,
/// <summary>(price - previous) / previous.</summary>
RocP,
/// <summary>price / previous.</summary>
RocR,
/// <summary>(price / previous) * 100.</summary>
RocR100
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rocPeriod1;
private readonly StrategyParam<RocTypes> _rocType1;
private readonly StrategyParam<int> _rocPeriod2;
private readonly StrategyParam<RocTypes> _rocType2;
private readonly StrategyParam<bool> _invert;
private decimal? _prevUp;
private decimal? _prevDn;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RocPeriod1 { get => _rocPeriod1.Value; set => _rocPeriod1.Value = value; }
public RocTypes RocType1 { get => _rocType1.Value; set => _rocType1.Value = value; }
public int RocPeriod2 { get => _rocPeriod2.Value; set => _rocPeriod2.Value = value; }
public RocTypes RocType2 { get => _rocType2.Value; set => _rocType2.Value = value; }
public bool Invert { get => _invert.Value; set => _invert.Value = value; }
public Roc2VgStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_rocPeriod1 = Param(nameof(RocPeriod1), 8).SetGreaterThanZero()
.SetDisplay("ROC Period 1", "Length of first ROC", "Indicator");
_rocType1 = Param(nameof(RocType1), RocTypes.Momentum)
.SetDisplay("ROC Type 1", "Type of first ROC", "Indicator");
_rocPeriod2 = Param(nameof(RocPeriod2), 14).SetGreaterThanZero()
.SetDisplay("ROC Period 2", "Length of second ROC", "Indicator");
_rocType2 = Param(nameof(RocType2), RocTypes.Momentum)
.SetDisplay("ROC Type 2", "Type of second ROC", "Indicator");
_invert = Param(nameof(Invert), false).SetDisplay("Invert", "Swap ROC lines", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUp = null;
_prevDn = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ind1 = CreateIndicator(RocType1, RocPeriod1);
var ind2 = CreateIndicator(RocType2, RocPeriod2);
var sub = SubscribeCandles(CandleType);
sub.Bind(ind1, ind2, Process).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, sub);
DrawIndicator(area, ind1);
DrawIndicator(area, ind2);
DrawOwnTrades(area);
}
}
private static IIndicator CreateIndicator(RocTypes type, int period)
{
return type == RocTypes.Momentum
? new Momentum { Length = period }
: new ROC { Length = period };
}
private decimal Transform(RocTypes type, decimal value)
{
return type switch
{
RocTypes.Momentum => value,
RocTypes.Roc => value * 100m,
RocTypes.RocP => value,
RocTypes.RocR => value + 1m,
RocTypes.RocR100 => (value + 1m) * 100m,
_ => value
};
}
private void Process(ICandleMessage candle, decimal v1, decimal v2)
{
if (candle.State != CandleStates.Finished || !IsFormedAndOnlineAndAllowTrading())
return;
var up = Transform(RocType1, Invert ? v2 : v1);
var dn = Transform(RocType2, Invert ? v1 : v2);
if (_prevUp is decimal pUp && _prevDn is decimal pDn)
{
if (pUp > pDn && up <= dn && Position <= 0)
BuyMarket();
else if (pUp < pDn && up >= dn && Position >= 0)
SellMarket();
}
_prevUp = up;
_prevDn = dn;
}
}
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 Momentum, RateOfChange
from StockSharp.Algo.Strategies import Strategy
class roc2_vg_strategy(Strategy):
def __init__(self):
super(roc2_vg_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._roc_period1 = self.Param("RocPeriod1", 8) \
.SetDisplay("ROC Period 1", "Length of first ROC", "Indicator")
self._roc_type1 = self.Param("RocType1", 0) \
.SetDisplay("ROC Type 1", "Type of first ROC", "Indicator")
self._roc_period2 = self.Param("RocPeriod2", 14) \
.SetDisplay("ROC Period 2", "Length of second ROC", "Indicator")
self._roc_type2 = self.Param("RocType2", 0) \
.SetDisplay("ROC Type 2", "Type of second ROC", "Indicator")
self._invert = self.Param("Invert", False) \
.SetDisplay("Invert", "Swap ROC lines", "General")
self._prev_up = None
self._prev_dn = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def roc_period1(self):
return self._roc_period1.Value
@property
def roc_type1(self):
return self._roc_type1.Value
@property
def roc_period2(self):
return self._roc_period2.Value
@property
def roc_type2(self):
return self._roc_type2.Value
@property
def invert(self):
return self._invert.Value
def OnReseted(self):
super(roc2_vg_strategy, self).OnReseted()
self._prev_up = None
self._prev_dn = None
def _create_indicator(self, roc_type, period):
if int(roc_type) == 0:
ind = Momentum()
ind.Length = period
return ind
else:
ind = RateOfChange()
ind.Length = period
return ind
def _transform(self, roc_type, value):
t = int(roc_type)
if t == 0:
return value
elif t == 1:
return value * 100.0
elif t == 2:
return value
elif t == 3:
return value + 1.0
elif t == 4:
return (value + 1.0) * 100.0
return value
def OnStarted2(self, time):
super(roc2_vg_strategy, self).OnStarted2(time)
ind1 = self._create_indicator(self.roc_type1, self.roc_period1)
ind2 = self._create_indicator(self.roc_type2, self.roc_period2)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ind1, ind2, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ind1)
self.DrawIndicator(area, ind2)
self.DrawOwnTrades(area)
def process_candle(self, candle, v1, v2):
if candle.State != CandleStates.Finished:
return
v1 = float(v1)
v2 = float(v2)
inv = bool(self.invert)
if inv:
up = self._transform(self.roc_type1, v2)
dn = self._transform(self.roc_type2, v1)
else:
up = self._transform(self.roc_type1, v1)
dn = self._transform(self.roc_type2, v2)
if self._prev_up is not None and self._prev_dn is not None:
if self._prev_up > self._prev_dn and up <= dn and self.Position <= 0:
self.BuyMarket()
elif self._prev_up < self._prev_dn and up >= dn and self.Position >= 0:
self.SellMarket()
self._prev_up = up
self._prev_dn = dn
def CreateClone(self):
return roc2_vg_strategy()