在 GitHub 上查看

平滑均线方向策略

该策略使用 StockSharp 高级 API 重写 MetaTrader 4 专家顾问 oc08_vy_m0moqesu15(位于 MQL/8615)。原始 EA 通过一条平滑移动平均线(SMMA)来确定方向,并为每笔交易设置固定的止损和止盈。C# 版本沿用相同的思路,同时采用 StockSharp 框架提供的标准组件。

交易思想

  • 方向判断: 当收盘价高于 SMMA 时认为趋势向上,收盘价低于 SMMA 时认为趋势向下。
  • 仓位调整: 策略始终保持单向持仓;一旦方向变化,会立即反向开仓以匹配新的趋势。
  • 风险控制: 每次开仓都会根据价格步长设置止损和止盈距离,使用 StartProtection 自动管理。
  • 执行方式: 信号在蜡烛收盘后以市价单执行,复现原策略中 OrdersTotal()==0 的行为。

工作流程

  1. 启动时订阅指定时间框架的蜡烛,并创建带有设定周期的 SmoothedMovingAverage 指标。
  2. 每当蜡烛收盘时,将指标值与收盘价进行比较。
  3. 如果收盘价高于 SMMA 且当前为空仓或持有空头,则发送市价买单,先平掉空头(若存在)再建立多头。
  4. 如果收盘价低于 SMMA 且当前为空仓或持有多头,则发送市价卖单,先平掉多头(若存在)再建立空头。
  5. 止损与止盈距离在启动时按 PriceStep 计算。若参数为 0,相应的保护将被禁用。
  6. 在支持图表的环境中,会自动绘制蜡烛、指标曲线和成交记录,便于可视化分析。

参数说明

参数 默认值 描述
StopLossPoints 100 止损距离(以价格步长计)。设为 0 表示不使用止损。
TakeProfitPoints 100 止盈距离(以价格步长计)。设为 0 表示不使用止盈。
MaPeriod 12 平滑移动平均线的周期,用于判断趋势方向。
TradeVolume 1 市价单的交易量,启动时同时写入 Strategy.Volume
CandleType 15 分钟 生成信号所使用的蜡烛类型(时间框架)。

所有参数都可以在 StockSharp Designer/Runner 中配置,并预置了优化范围以便批量测试。

与原始 EA 的差异

  • 取消了按可用保证金动态计算手数 (Lots/Prots),改用固定的 TradeVolume 参数,便于在 StockSharp 投资组合模型中复现。
  • 止损与止盈由 StartProtection 管理,而不是手动修改订单,效果与原逻辑保持一致。
  • 策略忽略未收盘的蜡烛,与 MQ4 中的 New_Bar 变量行为相同,防止提前下单。

使用建议

  • 请确认所连接的证券提供有效的 PriceStep。若未设置,策略将在计算止损/止盈时退回使用 1
  • 指标周期会在每根蜡烛结束时与当前参数同步,可在运行中调整,无需重新启动。
  • 若想最大程度地重现原 EA,请使用相同的图表时间框架,并根据需求设定合适的交易量。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Smoothed MA directional strategy. Goes long when price is above the MA, short when below.
/// </summary>
public class SmoothedMaDirectionalStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<DataType> _candleType;

	public SmoothedMaDirectionalStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 12)
			.SetDisplay("MA Period", "Number of bars for the moving average.", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used for price analysis.", "General");
	}

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ma = new SimpleMovingAverage { Length = MaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ma, ProcessCandle)
			.Start();

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

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

		var closePrice = candle.ClosePrice;

		if (closePrice > maValue && Position <= 0)
		{
			// Price above MA - go long
			if (Position < 0)
				BuyMarket(); // Close short
			BuyMarket(); // Open long
		}
		else if (closePrice < maValue && Position >= 0)
		{
			// Price below MA - go short
			if (Position > 0)
				SellMarket(); // Close long
			SellMarket(); // Open short
		}
	}
}