在 GitHub 上查看

Wajdyss MA Expert 策略

概览

Wajdyss MA Expert 策略 是 MetaTrader 4 专家顾问 “wajdyss MA expert v3” 的 C# 版本。它比较两条可分别设置周期、算法、偏移以及价格类型的移动平均线。当快线向上穿越慢线时开多单,向下穿越时开空单。移植版本完整保留了原始 EA 的资金管理、可选的反向仓位自动平仓以及日末/周末强制平仓功能。

交易逻辑

  1. 订阅所选的 CandleType(默认 15 分钟)并按各自的 MovingAverageMethodPriceSource 计算快慢移动平均线。
  2. 仅对已完成的 K 线保存指标数值。当快线(包含其 Shift 设置)在上一根收盘价上方且在两根之前位于慢线下方时触发看多信号;反之触发看空信号。
  3. 新仓位之间必须间隔至少一个完整周期,与 MT4 版本使用全局变量控制节奏的逻辑一致。
  4. 启用 AutoCloseOpposite 时,会先撤销挂单并在一笔市价单中反转仓位,订单数量包含当前相反方向的持仓,以确保立即翻仓。
  5. 应用日内与周五收盘过滤器。到达 DailyCloseHour/DailyCloseMinuteFridayCloseHour/FridayCloseMinute 后会平掉所有仓位并禁止新交易,直到下一交易日。

风险与资金管理

  • TakeProfitPipsStopLossPipsTrailingStopPips 以完整点数表示,程序根据合约的最小报价步长转换为价格并通过 StartProtection 触发市价止盈止损/跟踪止损,复制原版的保护机制。
  • UseMoneyManagement 复刻 MT4 的手数计算:volume = (account_balance / BalanceReference) * InitialVolume,并根据交易所的最小增量、最小/最大手数自动调整。
  • 关闭资金管理后,直接使用 InitialVolume 作为下单数量。

参数

参数 类型 默认值 说明
FastPeriod int 10 快速移动平均线的周期。
FastShift int 0 快速均线在比较时的偏移条数。
FastMethod MovingAverageMethod Ema 快速均线的算法(SmaEmaSmmaLwma)。
FastPriceType PriceSource Close 快速均线使用的价格(CloseOpenHighLowMedianTypicalWeighted)。
SlowPeriod int 20 慢速移动平均线的周期。
SlowShift int 0 慢速均线的偏移条数。
SlowMethod MovingAverageMethod Ema 慢速均线的算法。
SlowPriceType PriceSource Close 慢速均线使用的价格类型。
TakeProfitPips decimal 100 止盈距离(点),0 表示禁用。
StopLossPips decimal 50 止损距离(点),0 表示禁用。
TrailingStopPips decimal 0 跟踪止损距离(点),0 表示禁用。
AutoCloseOpposite bool true 新信号出现前是否自动平掉反向仓位。
InitialVolume decimal 0.1 资金管理前的基础下单手数。
UseMoneyManagement bool true 是否启用按余额缩放手数。
BalanceReference decimal 1000 计算动态手数时的余额分母。
DailyCloseHour int 23 每日强制平仓的小时(0-23)。
DailyCloseMinute int 45 每日强制平仓的分钟。
FridayCloseHour int 22 周五停止交易的小时(0-23)。
FridayCloseMinute int 45 周五停止交易的分钟。
CandleType DataType 15m 时间框架 用于指标计算与冷却计时的 K 线类型。

说明

  • 策略完全基于 StockSharp 的高级 API:使用 SubscribeCandles 处理 K 线、指标绑定计算移动平均线,并借助 StartProtection 管理止盈止损及跟踪止损。
  • 平仓操作采用市价单,模拟 MT4 EA 中立即关闭反向挂单与持仓的方式。
  • 本目录仅包含 C# 实现,不提供 Python 版本。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Wajdyss MA Expert: Fast/slow EMA crossover with ATR stops.
/// </summary>
public class WajdyssMaExpertStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

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

	public WajdyssMaExpertStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 20)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <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 fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

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

		// Entry: EMA crossover
		if (Position == 0)
		{
			if (_prevFast <= _prevSlow && fastVal > slowVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevFast >= _prevSlow && fastVal < slowVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}