Simplest DeMarker 策略
概述
Simplest DeMarker 策略复刻了原始 MetaTrader 智能交易系统的逻辑。它监测 DeMarker 振荡指标,以判断价格动量是否离开超买或超卖区域。当指标重新回到中性区间时,策略按照预期的反转方向建立仓位,并通过可配置的止损和止盈距离来控制风险。
核心逻辑
- 订阅所选周期的K线,并使用设定的周期计算 DeMarker 指标。
- 当上一根 DeMarker 值高于超买阈值时标记为超买,当低于超卖阈值时标记为超卖。
- 当指标重新回到中性区间时产生信号:
- 若指标从超买区间下穿阈值,则开空。
- 若指标从超卖区间上穿阈值,则开多。
- 同一时间仅持有一笔仓位。启用
Trade On Bar Open参数时,订单会延迟到下一根K线开盘发送;否则在当前K线收盘后立即入场。 - 通过
StartProtection服务设置止损和止盈,以模拟 MQL 版本中的固定距离。
参数
- Volume – 订单手数/合约数量。
- DeMarker Period – DeMarker 指标周期。
- Overbought Level – 判定超买区域的上限阈值。
- Oversold Level – 判定超卖区域的下限阈值。
- Trade On Bar Open – 若启用,则在下一根K线开盘时入场。
- Stop Loss Points – 止损距离(价格点)。
- Take Profit Points – 止盈距离(价格点)。
- Candle Type – 用于计算的K线类型/周期。
风险控制
- 使用
StartProtection自动注册止损和止盈,将距离转换为价格点。 - 任意时刻仅保留一笔仓位,存在持仓时会忽略新的信号。
图表元素
- 所订阅数据的价格K线。
- DeMarker 指标曲线。
- 自有交易标记,用于验证进出场位置。
备注
- 建议选择流动性较好的交易品种,以保证止损和止盈的执行质量。
Trade On Bar Open参数用于近似还原原始智能交易系统在新K线出现时入场的行为。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Simplest DeMarker strategy: DeMarker oscillator crossover.
/// Buys when DeMarker crosses above oversold, sells when crosses below overbought.
/// </summary>
public class SimplestDeMarkerStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _demarkerPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevValue;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int DemarkerPeriod { get => _demarkerPeriod.Value; set => _demarkerPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public SimplestDeMarkerStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_demarkerPeriod = Param(nameof(DemarkerPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("DeMarker Period", "DeMarker period", "Indicators");
_oversold = Param(nameof(Oversold), 0.2m)
.SetDisplay("Oversold", "DeMarker oversold level", "Signals");
_overbought = Param(nameof(Overbought), 0.8m)
.SetDisplay("Overbought", "DeMarker overbought level", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevValue = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevValue = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var demarker = new DeMarker { Length = DemarkerPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(demarker, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal demarkerValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevValue < Oversold && demarkerValue >= Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevValue > Overbought && demarkerValue <= Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevValue = demarkerValue;
_hasPrev = true;
}
}
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 DeMarker
from StockSharp.Algo.Strategies import Strategy
class simplest_de_marker_strategy(Strategy):
def __init__(self):
super(simplest_de_marker_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._demarker_period = self.Param("DemarkerPeriod", 14)
self._oversold = self.Param("Oversold", 0.2)
self._overbought = self.Param("Overbought", 0.8)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_value = 0.0
self._candles_since_trade = 4
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DemarkerPeriod(self):
return self._demarker_period.Value
@DemarkerPeriod.setter
def DemarkerPeriod(self, value):
self._demarker_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(simplest_de_marker_strategy, self).OnReseted()
self._prev_value = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
def OnStarted2(self, time):
super(simplest_de_marker_strategy, self).OnStarted2(time)
self._prev_value = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
demarker = DeMarker()
demarker.Length = self.DemarkerPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(demarker, self._process_candle).Start()
def _process_candle(self, candle, demarker_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
dm_val = float(demarker_value)
if self._has_prev:
if self._prev_value < self.Oversold and dm_val >= self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif self._prev_value > self.Overbought and dm_val <= self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_value = dm_val
self._has_prev = True
def CreateClone(self):
return simplest_de_marker_strategy()