XROC2 VG Zeitfilter-Strategie
Diese Strategie repliziert den MetaTrader Expert Advisor Exp_XROC2_VG_Tm unter Verwendung der StockSharp High-Level-API. Sie erstellt zwei geglättete Rate-of-Change-Kurven (ROC) und eröffnet konträre Trades, wenn die schnellere Kurve die langsamere kreuzt. Ein Handelsfilter und optionale Schutzziele reproduzieren die ursprünglichen Money-Management-Einstellungen.
Handelslogik
- Zwei ROC-Werte werden aus dem Schlusskurs mit unabhängigen Lookback-Werten berechnet.
- Jeder ROC-Stream wird mit einer konfigurierbaren Moving-Average-Methode geglättet.
- Signale werden auf einem verschobenen Barindex ausgewertet, was das ursprüngliche
SignalBar-Verhalten nachahmt. - Wenn die schnelle Linie auf dem vorherigen Bar über der langsamen lag, aber auf dem Signalbar darunter fällt, schließt die Strategie jede Short-Position und kann eine Long-Position eröffnen.
- Wenn die schnelle Linie auf dem vorherigen Bar unter der langsamen lag, aber auf dem Signalbar darüber steigt, schließt die Strategie jede Long-Position und kann eine Short-Position eröffnen.
- Ein optionales Handelsfenster kann alle Positionen außerhalb der erlaubten Sitzung schließen, bevor neue Trades platziert werden.
Die Orderseite wechselt erst, nachdem die vorherige Position vollständig geschlossen ist, was die MetaTrader-Trade-Algorithmen imitiert.
Indikatoren
- Schneller ROC – Momentum, Prozent oder Verhältnis der Preisänderung über
RocPeriod1Bars, geglättet mitSmoothMethod1und LängeSmoothLength1. - Langsamer ROC – Gleiche Berechnung über
RocPeriod2Bars, geglättet mitSmoothMethod2und LängeSmoothLength2. - Unterstützte Glättungsmethoden: Einfache, Exponentielle, Geglättete (RMA) und Gewichtete gleitende Durchschnitte. Die originalen JJMA/VIDYA/AMA-Optionen werden durch exponentielle Glättung approximiert.
Risikomanagement
StopLossundTakeProfitgeben optionale Exits mit fester Distanz in absoluten Preiseinheiten an. Wenn einer der Schwellenwerte erreicht wird, wird die Position sofort geschlossen.OrderVolumedefiniert die Größe aller neuen Positionen.- Indikatorbasierte Exits können Positionen auch dann schließen, wenn die Schutzziele deaktiviert sind.
Sitzungsfilter
UseTimeFilterschaltet das Tageszeit-Fenster ein/aus.StartTime/EndTimegeben die Sitzungsgrenzen an. Wenn das Intervall um Mitternacht herum liegt, wird das Fenster als zwei Segmente behandelt, genau wie in der MQL-Version.- Wenn eine Position noch offen ist, wenn das Fenster schließt, wird sie zum Marktpreis liquidiert, bevor die Strategie neue Einstiege auswertet.
Parameter
| Parameter | Beschreibung |
|---|---|
CandleType |
Kerzendatentyp für Berechnungen (Standard: 4-Stunden-Kerzen). |
RocPeriod1, RocPeriod2 |
Lookback-Längen für den schnellen und langsamen ROC-Stream. |
SmoothLength1, SmoothLength2 |
Glättungslängen für jeden Stream. |
SmoothMethod1, SmoothMethod2 |
Moving-Average-Typen, die auf die ROC-Outputs angewendet werden. |
RocType |
ROC-Berechnungsformel: Momentum, prozentuale Änderung oder Verhältnis. |
SignalShift |
Anzahl der abgeschlossenen Bars zurück, die zum Lesen der Signalwerte verwendet werden. |
AllowBuyOpen, AllowSellOpen |
Long-/Short-Positionen öffnen aktivieren oder deaktivieren. |
AllowBuyClose, AllowSellClose |
Indikatorbasierte Exits für Long-/Short-Positionen aktivieren oder deaktivieren. |
UseTimeFilter |
Aktiviert das Handelssitzungsfenster. |
StartTime, EndTime |
Sitzungsstart- und -endzeiten. |
OrderVolume |
Volumen für jeden neuen Trade. |
StopLoss, TakeProfit |
Optionale absolute Abstände für Schutz-Exits. |
Implementierungshinweise
- Die Strategie hält kurze Historien von Preisen und geglätteten Werten, anstatt Indikator-Buffer zu verwenden, was den ursprünglichen
SignalBar-Offset reproduziert, ohne sich aufGetValuezu verlassen. - JJMA, VIDYA und AMA-Glättung aus dem MQL-Indikator werden auf exponentielle Glättung gemappt, um im Standard-Indikatorset von StockSharp zu bleiben.
- Alle Kommentare im Code sind auf Englisch und der Namespace folgt den Repository-Richtlinien.
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>
/// XROC2 VG with time filter strategy converted from MetaTrader 5.
/// </summary>
public class Xroc2VgTmStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rocPeriod1;
private readonly StrategyParam<int> _rocPeriod2;
private readonly StrategyParam<int> _smoothLength1;
private readonly StrategyParam<int> _smoothLength2;
private readonly StrategyParam<SmoothingMethods> _smoothMethod1;
private readonly StrategyParam<SmoothingMethods> _smoothMethod2;
private readonly StrategyParam<RocCalculationTypes> _rocType;
private readonly StrategyParam<int> _signalShift;
private readonly StrategyParam<bool> _allowBuyOpen;
private readonly StrategyParam<bool> _allowSellOpen;
private readonly StrategyParam<bool> _allowBuyClose;
private readonly StrategyParam<bool> _allowSellClose;
private readonly StrategyParam<bool> _useTimeFilter;
private readonly StrategyParam<TimeSpan> _startTime;
private readonly StrategyParam<TimeSpan> _endTime;
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly List<decimal> _closeHistory = new();
private readonly List<decimal> _fastHistory = new();
private readonly List<decimal> _slowHistory = new();
private IIndicator _smoothFast;
private IIndicator _smoothSlow;
private decimal? _longEntryPrice;
private decimal? _shortEntryPrice;
/// <summary>
/// Rate-of-change calculation mode.
/// </summary>
public enum RocCalculationTypes
{
/// <summary>Momentum (difference between closes).</summary>
Momentum,
/// <summary>Rate of change in percent.</summary>
RateOfChange,
/// <summary>Relative rate of change (fraction).</summary>
Percent,
/// <summary>Price ratio.</summary>
Ratio,
/// <summary>Price ratio scaled by 100.</summary>
RatioPercent
}
/// <summary>
/// Smoothing method used for ROC lines.
/// </summary>
public enum SmoothingMethods
{
/// <summary>Simple moving average.</summary>
Simple,
/// <summary>Exponential moving average.</summary>
Exponential,
/// <summary>Smoothed moving average.</summary>
Smoothed,
/// <summary>Weighted moving average.</summary>
Weighted
}
/// <summary>
/// Initializes a new instance of the <see cref="Xroc2VgTmStrategy"/> class.
/// </summary>
public Xroc2VgTmStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
_rocPeriod1 = Param(nameof(RocPeriod1), 5)
.SetGreaterThanZero()
.SetDisplay("Fast ROC Period", "Lookback for the first ROC line", "Indicator")
;
_rocPeriod2 = Param(nameof(RocPeriod2), 10)
.SetGreaterThanZero()
.SetDisplay("Slow ROC Period", "Lookback for the second ROC line", "Indicator")
;
_smoothLength1 = Param(nameof(SmoothLength1), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Smoothing", "Smoothing length for the first line", "Indicator");
_smoothLength2 = Param(nameof(SmoothLength2), 5)
.SetGreaterThanZero()
.SetDisplay("Slow Smoothing", "Smoothing length for the second line", "Indicator");
_smoothMethod1 = Param(nameof(SmoothMethod1), SmoothingMethods.Exponential)
.SetDisplay("Fast Method", "Smoothing method for the first line", "Indicator");
_smoothMethod2 = Param(nameof(SmoothMethod2), SmoothingMethods.Exponential)
.SetDisplay("Slow Method", "Smoothing method for the second line", "Indicator");
_rocType = Param(nameof(RocType), RocCalculationTypes.Momentum)
.SetDisplay("ROC Mode", "Calculation used for rate of change", "Indicator");
_signalShift = Param(nameof(SignalShift), 0)
.SetNotNegative()
.SetDisplay("Signal Shift", "Bars back to read the signals", "Logic");
_allowBuyOpen = Param(nameof(AllowBuyOpen), true)
.SetDisplay("Allow Long Entry", "Enable opening long positions", "Trading");
_allowSellOpen = Param(nameof(AllowSellOpen), true)
.SetDisplay("Allow Short Entry", "Enable opening short positions", "Trading");
_allowBuyClose = Param(nameof(AllowBuyClose), true)
.SetDisplay("Allow Long Exit", "Enable closing long positions by indicator", "Trading");
_allowSellClose = Param(nameof(AllowSellClose), true)
.SetDisplay("Allow Short Exit", "Enable closing short positions by indicator", "Trading");
_useTimeFilter = Param(nameof(UseTimeFilter), false)
.SetDisplay("Use Time Filter", "Restrict trading to a time window", "Timing");
_startTime = Param(nameof(StartTime), TimeSpan.Zero)
.SetDisplay("Start Time", "Session start time", "Timing");
_endTime = Param(nameof(EndTime), new TimeSpan(23, 59, 0))
.SetDisplay("End Time", "Session end time", "Timing");
_orderVolume = Param(nameof(OrderVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Volume for new positions", "Trading");
_stopLoss = Param(nameof(StopLoss), 0m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Protective stop distance in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 0m)
.SetNotNegative()
.SetDisplay("Take Profit", "Target distance in price units", "Risk");
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Lookback of the first ROC line.
/// </summary>
public int RocPeriod1
{
get => _rocPeriod1.Value;
set => _rocPeriod1.Value = value;
}
/// <summary>
/// Lookback of the second ROC line.
/// </summary>
public int RocPeriod2
{
get => _rocPeriod2.Value;
set => _rocPeriod2.Value = value;
}
/// <summary>
/// Smoothing length applied to the first line.
/// </summary>
public int SmoothLength1
{
get => _smoothLength1.Value;
set => _smoothLength1.Value = value;
}
/// <summary>
/// Smoothing length applied to the second line.
/// </summary>
public int SmoothLength2
{
get => _smoothLength2.Value;
set => _smoothLength2.Value = value;
}
/// <summary>
/// Smoothing method for the first line.
/// </summary>
public SmoothingMethods SmoothMethod1
{
get => _smoothMethod1.Value;
set => _smoothMethod1.Value = value;
}
/// <summary>
/// Smoothing method for the second line.
/// </summary>
public SmoothingMethods SmoothMethod2
{
get => _smoothMethod2.Value;
set => _smoothMethod2.Value = value;
}
/// <summary>
/// Type of ROC calculation.
/// </summary>
public RocCalculationTypes RocType
{
get => _rocType.Value;
set => _rocType.Value = value;
}
/// <summary>
/// Number of bars back used for signal evaluation.
/// </summary>
public int SignalShift
{
get => _signalShift.Value;
set => _signalShift.Value = value;
}
/// <summary>
/// Enables long entries.
/// </summary>
public bool AllowBuyOpen
{
get => _allowBuyOpen.Value;
set => _allowBuyOpen.Value = value;
}
/// <summary>
/// Enables short entries.
/// </summary>
public bool AllowSellOpen
{
get => _allowSellOpen.Value;
set => _allowSellOpen.Value = value;
}
/// <summary>
/// Enables closing long positions by indicator signals.
/// </summary>
public bool AllowBuyClose
{
get => _allowBuyClose.Value;
set => _allowBuyClose.Value = value;
}
/// <summary>
/// Enables closing short positions by indicator signals.
/// </summary>
public bool AllowSellClose
{
get => _allowSellClose.Value;
set => _allowSellClose.Value = value;
}
/// <summary>
/// Turns the time filter on or off.
/// </summary>
public bool UseTimeFilter
{
get => _useTimeFilter.Value;
set => _useTimeFilter.Value = value;
}
/// <summary>
/// Trading session start time.
/// </summary>
public TimeSpan StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// Trading session end time.
/// </summary>
public TimeSpan EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>
/// Order volume used for new positions.
/// </summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>
/// Protective stop distance in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take-profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closeHistory.Clear();
_fastHistory.Clear();
_slowHistory.Clear();
_smoothFast = null;
_smoothSlow = null;
_longEntryPrice = null;
_shortEntryPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_smoothFast = CreateSmoothingIndicator(SmoothMethod1, SmoothLength1);
_smoothSlow = CreateSmoothingIndicator(SmoothMethod2, SmoothLength2);
_closeHistory.Clear();
_fastHistory.Clear();
_slowHistory.Clear();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _smoothFast);
DrawIndicator(area, _smoothSlow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var capacity = Math.Max(Math.Max(RocPeriod1, RocPeriod2) + SignalShift + 5, 8);
UpdateHistory(_closeHistory, candle.ClosePrice, capacity);
var fastRoc = CalculateRoc(RocPeriod1);
var slowRoc = CalculateRoc(RocPeriod2);
if (fastRoc is null || slowRoc is null)
return;
var fastValue = _smoothFast.Process(new DecimalIndicatorValue(_smoothFast, fastRoc.Value, candle.OpenTime) { IsFinal = true });
var slowValue = _smoothSlow.Process(new DecimalIndicatorValue(_smoothSlow, slowRoc.Value, candle.OpenTime) { IsFinal = true });
// Skip until we have enough data for both smoothing indicators
var fastDecimal = fastValue.GetValue<decimal>();
var slowDecimal = slowValue.GetValue<decimal>();
var historyCapacity = SignalShift + 3;
UpdateHistory(_fastHistory, fastDecimal, historyCapacity);
UpdateHistory(_slowHistory, slowDecimal, historyCapacity);
if (_fastHistory.Count <= SignalShift + 1 || _slowHistory.Count <= SignalShift + 1)
return;
var fastCurrent = _fastHistory[SignalShift];
var fastPrevious = _fastHistory[SignalShift + 1];
var slowCurrent = _slowHistory[SignalShift];
var slowPrevious = _slowHistory[SignalShift + 1];
var buyOpenSignal = AllowBuyOpen && fastPrevious <= slowPrevious && fastCurrent > slowCurrent;
var sellOpenSignal = AllowSellOpen && fastPrevious >= slowPrevious && fastCurrent < slowCurrent;
var buyCloseSignal = AllowBuyClose && fastCurrent < slowCurrent;
var sellCloseSignal = AllowSellClose && fastCurrent > slowCurrent;
var tradeAllowed = !UseTimeFilter || IsWithinTradeWindow(candle.OpenTime);
if (UseTimeFilter && !tradeAllowed && Position != 0)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
ResetPositionState();
return;
}
if (TryApplyRiskManagement(candle))
return;
if (sellCloseSignal && Position < 0)
{
BuyMarket();
ResetPositionState();
return;
}
if (buyCloseSignal && Position > 0)
{
SellMarket();
ResetPositionState();
return;
}
if (!tradeAllowed)
return;
//if (!IsFormedAndOnlineAndAllowTrading())
// return;
if (Position != 0)
return;
if (buyOpenSignal)
{
BuyMarket();
_longEntryPrice = candle.ClosePrice;
_shortEntryPrice = null;
}
else if (sellOpenSignal)
{
SellMarket();
_shortEntryPrice = candle.ClosePrice;
_longEntryPrice = null;
}
}
private bool TryApplyRiskManagement(ICandleMessage candle)
{
if (StopLoss <= 0m && TakeProfit <= 0m)
return false;
if (Position > 0 && _longEntryPrice is decimal longEntry)
{
if (StopLoss > 0m)
{
var stopLevel = longEntry - StopLoss;
if (candle.LowPrice <= stopLevel)
{
SellMarket();
ResetPositionState();
return true;
}
}
if (TakeProfit > 0m)
{
var targetLevel = longEntry + TakeProfit;
if (candle.HighPrice >= targetLevel)
{
SellMarket();
ResetPositionState();
return true;
}
}
}
else if (Position < 0 && _shortEntryPrice is decimal shortEntry)
{
if (StopLoss > 0m)
{
var stopLevel = shortEntry + StopLoss;
if (candle.HighPrice >= stopLevel)
{
BuyMarket();
ResetPositionState();
return true;
}
}
if (TakeProfit > 0m)
{
var targetLevel = shortEntry - TakeProfit;
if (candle.LowPrice <= targetLevel)
{
BuyMarket();
ResetPositionState();
return true;
}
}
}
return false;
}
private decimal? CalculateRoc(int period)
{
if (period <= 0 || _closeHistory.Count <= period)
return null;
var current = _closeHistory[0];
var previous = _closeHistory[period];
if (previous == 0m && (RocType == RocCalculationTypes.RateOfChange || RocType == RocCalculationTypes.Percent || RocType == RocCalculationTypes.Ratio || RocType == RocCalculationTypes.RatioPercent))
return null;
return RocType switch
{
RocCalculationTypes.Momentum => current - previous,
RocCalculationTypes.RateOfChange => previous == 0m ? null : (decimal?)((current / previous) - 1m) * 100m,
RocCalculationTypes.Percent => previous == 0m ? null : (decimal?)((current - previous) / previous),
RocCalculationTypes.Ratio => previous == 0m ? null : (decimal?)(current / previous),
RocCalculationTypes.RatioPercent => previous == 0m ? null : (decimal?)(current / previous * 100m),
_ => current - previous
};
}
private bool IsWithinTradeWindow(DateTimeOffset time)
{
var currentMinutes = time.TimeOfDay.TotalMinutes;
var startMinutes = StartTime.TotalMinutes;
var endMinutes = EndTime.TotalMinutes;
if (startMinutes < endMinutes)
return currentMinutes >= startMinutes && currentMinutes < endMinutes;
if (startMinutes > endMinutes)
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
return false;
}
private static void UpdateHistory(List<decimal> history, decimal value, int capacity)
{
history.Insert(0, value);
if (history.Count > capacity)
history.RemoveAt(history.Count - 1);
}
private void ResetPositionState()
{
_longEntryPrice = null;
_shortEntryPrice = null;
}
private static IIndicator CreateSmoothingIndicator(SmoothingMethods method, int length)
{
IIndicator indicator = method switch
{
SmoothingMethods.Simple => new SMA { Length = length },
SmoothingMethods.Smoothed => new EMA { Length = length },
SmoothingMethods.Weighted => new SMA { Length = length },
_ => new EMA { Length = length }
};
return indicator;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import (
SimpleMovingAverage, ExponentialMovingAverage
)
from indicator_extensions import *
class xroc2_vg_tm_strategy(Strategy):
"""XROC2 VG with time filter: dual smoothed ROC crossover strategy."""
def __init__(self):
super(xroc2_vg_tm_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary timeframe", "General")
self._roc_period1 = self.Param("RocPeriod1", 5) \
.SetGreaterThanZero() \
.SetDisplay("Fast ROC Period", "Lookback for fast ROC", "Indicator")
self._roc_period2 = self.Param("RocPeriod2", 10) \
.SetGreaterThanZero() \
.SetDisplay("Slow ROC Period", "Lookback for slow ROC", "Indicator")
self._smooth_len1 = self.Param("SmoothLength1", 5) \
.SetGreaterThanZero() \
.SetDisplay("Fast Smoothing", "Smoothing for fast line", "Indicator")
self._smooth_len2 = self.Param("SmoothLength2", 5) \
.SetGreaterThanZero() \
.SetDisplay("Slow Smoothing", "Smoothing for slow line", "Indicator")
self._signal_shift = self.Param("SignalShift", 0) \
.SetDisplay("Signal Shift", "Bars back to read signals", "Logic")
self._allow_buy_open = self.Param("AllowBuyOpen", True) \
.SetDisplay("Allow Long Entry", "Enable long positions", "Trading")
self._allow_sell_open = self.Param("AllowSellOpen", True) \
.SetDisplay("Allow Short Entry", "Enable short positions", "Trading")
self._allow_buy_close = self.Param("AllowBuyClose", True) \
.SetDisplay("Allow Long Exit", "Enable closing longs", "Trading")
self._allow_sell_close = self.Param("AllowSellClose", True) \
.SetDisplay("Allow Short Exit", "Enable closing shorts", "Trading")
self._use_time_filter = self.Param("UseTimeFilter", False) \
.SetDisplay("Use Time Filter", "Restrict trading to time window", "Timing")
self._start_hour = self.Param("StartHour", 0) \
.SetDisplay("Start Hour", "Session start hour", "Timing")
self._start_minute = self.Param("StartMinute", 0) \
.SetDisplay("Start Minute", "Session start minute", "Timing")
self._end_hour = self.Param("EndHour", 23) \
.SetDisplay("End Hour", "Session end hour", "Timing")
self._end_minute = self.Param("EndMinute", 59) \
.SetDisplay("End Minute", "Session end minute", "Timing")
self._order_volume = self.Param("OrderVolume", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Order Volume", "Volume for new positions", "Trading")
self._stop_loss = self.Param("StopLoss", 0.0) \
.SetDisplay("Stop Loss", "Stop distance in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 0.0) \
.SetDisplay("Take Profit", "Target distance in price units", "Risk")
self._close_history = []
self._fast_history = []
self._slow_history = []
self._smooth_fast = None
self._smooth_slow = None
self._long_entry = None
self._short_entry = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def RocPeriod1(self):
return self._roc_period1.Value
@property
def RocPeriod2(self):
return self._roc_period2.Value
@property
def SmoothLength1(self):
return self._smooth_len1.Value
@property
def SmoothLength2(self):
return self._smooth_len2.Value
@property
def SignalShift(self):
return self._signal_shift.Value
@property
def AllowBuyOpen(self):
return self._allow_buy_open.Value
@property
def AllowSellOpen(self):
return self._allow_sell_open.Value
@property
def AllowBuyClose(self):
return self._allow_buy_close.Value
@property
def AllowSellClose(self):
return self._allow_sell_close.Value
@property
def UseTimeFilter(self):
return self._use_time_filter.Value
@property
def StartHour(self):
return self._start_hour.Value
@property
def StartMinute(self):
return self._start_minute.Value
@property
def EndHour(self):
return self._end_hour.Value
@property
def EndMinute(self):
return self._end_minute.Value
@property
def OrderVolume(self):
return self._order_volume.Value
@property
def StopLoss(self):
return self._stop_loss.Value
@property
def TakeProfit(self):
return self._take_profit.Value
def OnStarted2(self, time):
super(xroc2_vg_tm_strategy, self).OnStarted2(time)
self.Volume = self.OrderVolume
self._smooth_fast = ExponentialMovingAverage()
self._smooth_fast.Length = self.SmoothLength1
self._smooth_slow = ExponentialMovingAverage()
self._smooth_slow.Length = self.SmoothLength2
self._close_history = []
self._fast_history = []
self._slow_history = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
capacity = max(max(self.RocPeriod1, self.RocPeriod2) + self.SignalShift + 5, 8)
self._close_history.insert(0, close)
while len(self._close_history) > capacity:
self._close_history.pop()
fast_roc = self._calc_roc(self.RocPeriod1)
slow_roc = self._calc_roc(self.RocPeriod2)
if fast_roc is None or slow_roc is None:
return
fast_out = process_float(self._smooth_fast, fast_roc, candle.OpenTime, True)
slow_out = process_float(self._smooth_slow, slow_roc, candle.OpenTime, True)
fast_val = float(fast_out)
slow_val = float(slow_out)
hist_cap = self.SignalShift + 3
self._fast_history.insert(0, fast_val)
while len(self._fast_history) > hist_cap:
self._fast_history.pop()
self._slow_history.insert(0, slow_val)
while len(self._slow_history) > hist_cap:
self._slow_history.pop()
ss = self.SignalShift
if len(self._fast_history) <= ss + 1 or len(self._slow_history) <= ss + 1:
return
fc = self._fast_history[ss]
fp = self._fast_history[ss + 1]
sc = self._slow_history[ss]
sp = self._slow_history[ss + 1]
buy_open = self.AllowBuyOpen and fp <= sp and fc > sc
sell_open = self.AllowSellOpen and fp >= sp and fc < sc
buy_close = self.AllowBuyClose and fc < sc
sell_close = self.AllowSellClose and fc > sc
in_window = (not self.UseTimeFilter) or self._in_trade_window(candle.OpenTime)
if self.UseTimeFilter and not in_window and self.Position != 0:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._reset_state()
return
if self._try_risk(candle):
return
if sell_close and self.Position < 0:
self.BuyMarket()
self._reset_state()
return
if buy_close and self.Position > 0:
self.SellMarket()
self._reset_state()
return
if not in_window:
return
if self.Position != 0:
return
if buy_open:
self.BuyMarket()
self._long_entry = close
self._short_entry = None
elif sell_open:
self.SellMarket()
self._short_entry = close
self._long_entry = None
def _calc_roc(self, period):
if period <= 0 or len(self._close_history) <= period:
return None
current = self._close_history[0]
previous = self._close_history[period]
# Momentum mode (default)
return current - previous
def _try_risk(self, candle):
sl = float(self.StopLoss)
tp = float(self.TakeProfit)
if sl <= 0 and tp <= 0:
return False
if self.Position > 0 and self._long_entry is not None:
if sl > 0 and float(candle.LowPrice) <= self._long_entry - sl:
self.SellMarket()
self._reset_state()
return True
if tp > 0 and float(candle.HighPrice) >= self._long_entry + tp:
self.SellMarket()
self._reset_state()
return True
elif self.Position < 0 and self._short_entry is not None:
if sl > 0 and float(candle.HighPrice) >= self._short_entry + sl:
self.BuyMarket()
self._reset_state()
return True
if tp > 0 and float(candle.LowPrice) <= self._short_entry - tp:
self.BuyMarket()
self._reset_state()
return True
return False
def _in_trade_window(self, time):
start = TimeSpan(self.StartHour, self.StartMinute, 0)
end = TimeSpan(self.EndHour, self.EndMinute, 0)
current = time.TimeOfDay
if start < end:
return current >= start and current < end
if start > end:
return current >= start or current < end
return False
def _reset_state(self):
self._long_entry = None
self._short_entry = None
def OnReseted(self):
super(xroc2_vg_tm_strategy, self).OnReseted()
self._close_history = []
self._fast_history = []
self._slow_history = []
self._smooth_fast = None
self._smooth_slow = None
self._reset_state()
def CreateClone(self):
return xroc2_vg_tm_strategy()