XDerivative 策略
XDerivative 策略通过平滑的价格变化率来捕捉动量变化。原始的 MQL 专家将 Rate of Change 与 Jurik 平滑相结合来识别转折点。该版本使用 StockSharp 的内置指标实现相同的概念。
策略计算 RocPeriod 根柱的 Rate of Change,并使用长度为 MaLength 的 Jurik 移动平均进行平滑。当平滑后的导数在局部低点后向上转折时,策略开多或翻多;当在局部高点后向下转折时,策略开空或翻空。仓位由百分比止盈和止损管理。
详情
- 入场条件:
- 多头: 平滑导数在局部低点后向上转折。
- 空头: 平滑导数在局部高点后向下转折。
- 多空方向: Both directions.
- 出场条件: 反向转折或保护止损。
- 止损: Yes.
- 默认值:
RocPeriod= 34MaLength= 7TakeProfitPercent= 2StopLossPercent= 1CandleType= TimeSpan.FromHours(4)
- 过滤器:
- 类别: Trend following
- 方向: Both
- 指标: RateOfChange, JurikMovingAverage
- 止损: Yes
- 复杂度: Basic
- 时间框架: 4H
- 季节性: No
- 神经网络: No
- 背离: No
- 风险等级: Medium
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>
/// XDerivative strategy based on smoothed rate of change.
/// Enters long when the smoothed derivative forms a trough and short when it forms a peak.
/// </summary>
public class XDerivativeStrategy : Strategy
{
private readonly StrategyParam<int> _rocPeriod;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private JurikMovingAverage _jma;
private decimal? _prevValue;
private decimal? _prevPrevValue;
public int RocPeriod
{
get => _rocPeriod.Value;
set => _rocPeriod.Value = value;
}
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public XDerivativeStrategy()
{
_rocPeriod = Param(nameof(RocPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ROC Period", "Period for rate of change", "Parameters");
_maLength = Param(nameof(MaLength), 7)
.SetGreaterThanZero()
.SetDisplay("JMA Length", "Period for Jurik MA smoothing", "Parameters");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_jma = default;
_prevValue = null;
_prevPrevValue = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_jma = new JurikMovingAverage { Length = MaLength };
var roc = new RateOfChange { Length = RocPeriod };
Indicators.Add(_jma);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(roc, (candle, rocValue) =>
{
if (candle.State != CandleStates.Finished)
return;
var jmaResult = _jma.Process(rocValue, candle.OpenTime, true);
if (!jmaResult.IsFormed)
return;
var value = jmaResult.ToDecimal();
if (_prevValue is decimal prev && _prevPrevValue is decimal prev2)
{
var turnUp = prev < prev2 && value > prev;
var turnDown = prev > prev2 && value < prev;
if (turnUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (turnDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevPrevValue = _prevValue;
_prevValue = value;
})
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, roc);
DrawOwnTrades(area);
}
}
}
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 JurikMovingAverage, RateOfChange
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class x_derivative_strategy(Strategy):
def __init__(self):
super(x_derivative_strategy, self).__init__()
self._roc_period = self.Param("RocPeriod", 14) \
.SetDisplay("ROC Period", "Period for rate of change", "Parameters")
self._ma_length = self.Param("MaLength", 7) \
.SetDisplay("JMA Length", "Period for Jurik MA smoothing", "Parameters")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "Parameters")
self._jma = None
self._prev_value = None
self._prev_prev_value = None
@property
def roc_period(self):
return self._roc_period.Value
@property
def ma_length(self):
return self._ma_length.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(x_derivative_strategy, self).OnReseted()
self._jma = None
self._prev_value = None
self._prev_prev_value = None
def OnStarted2(self, time):
super(x_derivative_strategy, self).OnStarted2(time)
self._jma = JurikMovingAverage()
self._jma.Length = self.ma_length
roc = RateOfChange()
roc.Length = self.roc_period
self.Indicators.Add(self._jma)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(roc, self.on_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, roc)
self.DrawOwnTrades(area)
def on_candle(self, candle, roc_value):
if candle.State != CandleStates.Finished:
return
jma_result = process_float(self._jma, roc_value, candle.OpenTime, True)
if not jma_result.IsFormed:
return
value = float(jma_result)
if self._prev_value is not None and self._prev_prev_value is not None:
turn_up = self._prev_value < self._prev_prev_value and value > self._prev_value
turn_down = self._prev_value > self._prev_prev_value and value < self._prev_value
if turn_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif turn_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_value = self._prev_value
self._prev_value = value
def CreateClone(self):
return x_derivative_strategy()