在 GitHub 上查看

AIS1 交易机器人(MQL/8700 转换版)

概述

AIS1 Trading Robot 源自 MQL/8700/AIS1.MQ4 的 MetaTrader 4 智能交易系统。原策略面向 EURUSD 日线突破交易,并使用多周期区间来计算止损、止盈和跟踪止损。本 C# 实现完全保留了原始结构,同时通过 StockSharp 参数接口公开了所有关键设置,便于调优与自动化运行。

交易逻辑

  • 时间框架
    • 主周期:1 日蜡烛,用于判定突破信号、初始止盈和止损。
    • 次周期:4 小时蜡烛,用于计算动态跟踪止损距离。
  • 入场条件
    • 多头突破:前一根日线收盘价高于其高低价中点,并且当前卖价向上突破上一日高点。
    • 空头突破:前一根日线收盘价低于中点,并且当前买价跌破上一日低点。
    • 任意时刻仅允许持有一个方向的仓位;持仓未平仓时忽略新的反向信号。
  • 初始风险与收益
    • 止损价 = 上一日高/低价 ± StopFactor × 日线振幅
    • 止盈价 = 入场价 ± TakeFactor × 日线振幅
    • StopBufferTicks(若 > 0)会作为缓冲,用于满足经纪商最小止损距离要求。
  • 跟踪止损
    • 使用最近一根 4 小时蜡烛的振幅乘以 TrailFactor 作为移动距离。
    • 只有在价格较当前止损额外前进 TrailStepMultiplier × 点差,且距离目标仍保留缓冲时才会更新止损。
    • 当权益跌破保留阈值时,跟踪止损自动暂停,避免在回撤期间频繁操作。
  • 资金管理
    • 仓位大小 = OrderReserve × 当前权益 ÷ (入场价与止损价之间的货币风险)
    • 结果会被调整至交易所允许的最小/最大交易量以及交易步进。
    • 策略跟踪权益峰值,当权益低于 AccountReserve - OrderReserve 所定义的保留区间时停止开仓。
  • 节奏控制
    • 每次进场或跟踪更新后都会进入 5 秒冷却期,复刻原 EA 的节流逻辑。

参数一览

参数 默认值 说明
AccountReserve 0.20 账户权益中必须保留的比例,用于计算允许的最大回撤。
OrderReserve 0.04 每笔交易可使用的权益比例,同时用于计算下单手数。
PrimaryCandleType 日线 用于突破判断及初始止盈止损的蜡烛类型。
SecondaryCandleType 4 小时 用于推导跟踪止损距离的蜡烛类型。
TakeFactor 0.8 应用于日线振幅的止盈倍数。
StopFactor 1.0 应用于日线振幅的止损倍数。
TrailFactor 5.0 应用于 4 小时振幅的跟踪止损倍数。
TrailStepMultiplier 1.0 点差乘数,用于限制跟踪止损的触发频率。
StopBufferTicks 0 额外的价格步进缓冲,用于满足交易所/经纪商的最小距离要求。

使用提示

  1. 启动前需绑定目标 证券(默认为 EURUSD)以及 投资组合,确保能够获取账户权益信息。
  2. 必须同时提供日线与 4 小时蜡烛数据,否则突破和跟踪模块无法启动。
  3. 策略会订阅盘口以获取最新买卖价;若缺少盘口,则回退到蜡烛收盘价作为近似值。
  4. 与原版 EA 修改服务器端止损不同,本实现通过监控价格并在触发时直接以市价平仓。
  5. AccountReserve、冷却时间与仓位算法均可调整,以适配不同经纪商的保证金与限制规则。

与原 MQL 的差异

  • 保护性止损与止盈采用手动平仓实现,而非修改订单;行为与原 EA 在服务器端执行效果等价。
  • 资金换算依赖 Security.PriceStepSecurity.StepPrice 元数据;若缺失,则退化为 1:1 换算,使用前请确认合约参数。
  • 为了方便调试与优化,代码中加入了详细注释以及完整的参数描述。

环境要求

  • 可访问日线与 4 小时蜡烛数据的 StockSharp 高级 API。
  • 已配置的交易连接,用于报单与获取账户权益。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AIS1 breakout strategy with ATR-based stops and trailing.
/// Trades breakouts above/below EMA with ATR-based risk management.
/// </summary>
public class Ais1TradingRobotStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _takeFactor;
	private readonly StrategyParam<decimal> _stopFactor;

	private decimal _entryPrice;

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

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "Period for trend EMA.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 20)
			.SetDisplay("ATR Length", "Period for ATR.", "Indicators");

		_takeFactor = Param(nameof(TakeFactor), 3.0m)
			.SetDisplay("Take Factor", "ATR multiplier for take profit.", "Risk");

		_stopFactor = Param(nameof(StopFactor), 1.5m)
			.SetDisplay("Stop Factor", "ATR multiplier for stop loss.", "Risk");
	}

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

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

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

	public decimal TakeFactor
	{
		get => _takeFactor.Value;
		set => _takeFactor.Value = value;
	}

	public decimal StopFactor
	{
		get => _stopFactor.Value;
		set => _stopFactor.Value = value;
	}

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

		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_entryPrice = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (atrValue <= 0)
			return;

		var close = candle.ClosePrice;
		var takeDistance = atrValue * TakeFactor;
		var stopDistance = atrValue * StopFactor;

		// Position management with ATR-based stops
		if (Position > 0)
		{
			if (_entryPrice > 0)
			{
				if (close >= _entryPrice + takeDistance || close <= _entryPrice - stopDistance)
				{
					SellMarket();
				}
			}
		}
		else if (Position < 0)
		{
			if (_entryPrice > 0)
			{
				if (close <= _entryPrice - takeDistance || close >= _entryPrice + stopDistance)
				{
					BuyMarket();
				}
			}
		}

		// Entry: breakout above/below EMA
		if (Position == 0)
		{
			if (close > emaValue + atrValue * 1.5m)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < emaValue - atrValue * 1.5m)
			{
				_entryPrice = close;
				SellMarket();
			}
		}
	}
}