在 GitHub 上查看

TDSGlobal 4

概述

TDSGlobal 4 是 MetaTrader 4 专家顾问 “TDSGlobal 4” 的 StockSharp 版本。原始系统遵循 Alexander Elder 的三重筛选理念, 通过比较日线 MACD 主线的斜率与 Williams %R 指标的极值来过滤趋势,并仅在动量与震荡指标方向一致时布置突破挂单。此 次移植完整保留了该逻辑,同时加入精确的分钟级调度,使不同的外汇品种在错开的分钟触发,并提供可选的跟踪止损和可 配置的止盈管理。

策略逻辑

高级别滤波

  • MACD 斜率 – 对比最近两个完成的日线 MACD 主线值(快速 EMA 12、慢速 EMA 26、信号 EMA 9)。当最新值高于前一 个值时视为多头趋势,低于前一个值时视为空头,持平时不建立方向偏好。
  • Williams %R – 计算日线 Williams %R(周期 24)。只有当读数高于上限阈值(默认 −25,代表强势区间)时才允许布置多 头挂单;当读数低于下限阈值(默认 −75,代表弱势区间)时才允许布置空头挂单。

突破布单

  • 价格区间 – 每当日线完成时记录上一交易日的最高价与最低价,并在其外侧额外加上一个点(可通过 EntryBufferPips 调整)来设置新的止损挂单,忠实复现原 EA 的 ±1 point 偏移。
  • 距离保护 – 在发送挂单之前,策略会检查当前最优买/卖价与挂单价格之间的距离,要求至少达到 MinDistancePips (默认 16 点,等同于 EA 中 16Point 的限制),以避免在波动率极低时把订单放得过近。
  • 方向过滤 – 只有当 MACD 斜率为正且 Williams %R 满足多头条件时才会创建买入止损挂单;当 MACD 斜率为负且 Williams %R 满足空头条件时才会创建卖出止损挂单。

挂单维护

  • 每日重置 – 新的日线完成时会取消所有尚未成交的挂单,确保下一交易日从干净状态开始。如果过滤条件不满足,则该 日不会布置订单。
  • 每日一次评估 – 某一交易日的挂单只评估一次(无论是否真正下单)。若挂单被触发,另一侧挂单会被立刻撤销,以防 止同时持有多/空头寸。

风险管理

  • 保护性止损 – 多头仓位使用上一交易日低点下方的止损价,空头仓位则使用上一交易日高点上方的止损价,通过一分钟 触发流实时监测。
  • 止盈 – 可选的固定止盈,单位为点数,相对于实际成交价计算。将 TakeProfitPips 设为 0 即可停用,与原 EA 行为 一致。
  • 跟踪止损 – 当 TrailingStopPips 大于零时,策略会订阅 Level1 数据并读取最优买/卖价,在价格朝有利方向运行后启动 跟踪。当行情回落触及跟踪价时,仓位立即市价平仓。

调度机制

  • 分钟窗口 – 为避免多个货币对在同一时间发送挂单,原 EA 为每个主要品种分配了不同的分钟窗口。移植版本完全复现 此设计:USDCHF 使用分钟 0/8/16/24/32/40/48,GBPUSD 使用 2/10/18/26/34/42/50,USDJPY 使用 4/12/20/28/36/44/52, EURUSD 使用 6/14/22/30/38/46/54。其他品种默认允许任意分钟(0–59)。
  • 触发时间序列 – 通过订阅一分钟 K 线既负责调度挂单,也负责在盘中检测止损/止盈。信号评估每天仅在第一个符合窗 口的分钟执行一次。

参数

参数 说明 默认值
Volume 止损挂单的下单量。 1
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod MACD 斜率计算所用的参数。 12 / 26 / 9
WilliamsPeriod Williams %R 的计算周期。 24
WilliamsBuyLevel 允许多头挂单的上限阈值。 -25
WilliamsSellLevel 允许空头挂单的下限阈值。 -75
TakeProfitPips 止盈距离(点);0 表示禁用。 999
TrailingStopPips 跟踪止损距离(点);0 表示禁用。 10
EntryBufferPips 在前一日高/低点外加的偏移量。 1
MinDistancePips 当前报价到挂单价格的最小距离。 16
DailyCandleType 驱动 MACD 与 Williams %R 的日线周期。 1 日 K 线
TriggerCandleType 用于调度和风控的一分钟周期。 1 分钟 K 线

补充说明

  • C# 版本完全采用 StockSharp 的高阶接口(SubscribeCandlesBuyStopSellStop、Level1 绑定等),无需手动编写低 阶下单逻辑即可集成到平台。
  • 跟踪止损需要 Level1 数据支持,因为算法依赖最优买/卖价来更新和触发虚拟止损。
  • 该目录仅提供 C# 实现及多语言文档,不包含 Python 版本。
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// TDSGlobal 4: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class TdsGlobal4Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public TdsGlobal4Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 12)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 26)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}