在 GitHub 上查看

Trading Panel 批量策略

概述

TradingPanelBatchStrategy 是 MetaTrader 4 专家顾问 EA_TradingPanel 的 StockSharp 版本。原脚本提供了一个手动面板,交易者先设置批量下单的数量、每笔手数以及止损/止盈距离,然后点击 BUY 或 SELL 按钮。移植到 StockSharp 后,操作员只需在参数中指定 Direction,策略就会在下一根完整蜡烛关闭时按照设定方向批量发送市价单,并立即把方向重置为 None。

逻辑保持轻量,方便与外部信号或人工干预组合使用。所有订单都会继承可选的止损和止盈,距离以点(pip)表示,完全对应原 MQL 面板的风险控制。

工作流程

  1. 策略启动时根据 Security.PriceStep 计算 pip 大小。对于 1/3/5 位报价的外汇品种,会额外乘以 10,与 MetaTrader 将 point 转换为 pip 的方式一致。
  2. 若止损或止盈距离大于零,则调用 StartProtection,并使用市价单管理离场。
  3. 订阅 CandleType 参数指定的蜡烛序列。每当有蜡烛收盘,就检查当前的 Direction。
  4. 如果指定了方向且允许交易,策略会按照 NumberOfOrders 的数量、OrderVolume 的手数连续发送市价单。
  5. 批量下单完成后写入日志,并把 Direction 自动恢复为 None,等待下一次人工触发。

通过这种设计,策略在两次执行之间保持无状态。操作者可以在需要时把 Direction 改成 Buy 或 Sell,下一根完成的蜡烛就会触发新的订单批次,避免在未完成的数据上行动。

参数

名称 类型 默认值 说明
NumberOfOrders int 1 下一批要发送的市价单数量。
OrderVolume decimal 0.01 每一张市价单的交易量。
StopLossPips decimal 2 以 pip 表示的止损距离,会根据品种元数据转换为绝对价格。设为 0 表示不使用。
TakeProfitPips decimal 10 以 pip 表示的止盈距离。设为 0 表示不使用。
Direction TradeDirection None 下一次执行的方向,发送完毕后会自动重置为 None。
CandleType DataType TimeFrameCandle(1m) 用于触发执行的蜡烛类型。

说明

  • 运行前需要为 Security 配置正确的 PriceStep(以及 Decimals),否则 pip 转换将退化为常数 1。
  • StartProtection 使用市价单离场,以模拟原面板在触发止损/止盈时的处理方式。
  • 由于执行发生在完整蜡烛上,操作者可以在蜡烛收盘前更新 Direction,从而把外部分析或手动判断与批量下单同步。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Trading Panel Batch strategy: DEMA crossover.
/// Buys when fast DEMA crosses above slow DEMA, sells on cross below.
/// </summary>
public class TradingPanelBatchStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public TradingPanelBatchStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast DEMA", "Fast DEMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow DEMA", "Slow DEMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
		_hasPrev = true;
	}
}