在 GitHub 上查看

AIS2 交易机器人

概述

AIS2 交易机器人是从 MetaTrader 5 专家顾问移植的多周期突破策略。策略在较高周期(默认 15 分钟 K 线)上寻找突破方向,并使用更快的周期(默认 1 分钟)来动态调整跟踪止损。订单提交、风险预算以及止损管理完全遵循原始 MQ5 代码的逻辑,但基于 StockSharp 的高级策略 API 重新实现。

交易逻辑

  • 主信号 K 线:每当主周期产生一根收盘的 K 线时,策略会记录其最高价、最低价、收盘价、中点以及振幅。
  • 做多条件
    • 上一根 K 线的收盘价必须高于中点,表明多头力量占优。
    • 当前卖价需要突破前高并超过测得的点差(确认突破)。
    • 入场价取当前卖价,止损价为 高点 + 点差 − (振幅 × StopFactor),止盈价为 卖价 + (振幅 × TakeFactor)
    • 额外的安全检查会确保止损与止盈距离都大于配置的最小缓冲。
  • 做空条件
    • 上一根 K 线的收盘价必须低于中点。
    • 当前买价要跌破前低,确认下破。
    • 入场价取当前买价,止损价为 低点 + (振幅 × StopFactor),止盈价为 买价 − (振幅 × TakeFactor)
  • 仓位处理:仅在空仓或持有反向仓位时开新单。新单的下单量会自动覆盖反向仓位后再建立目标方向仓位。

订单管理

  • 跟踪止损:次级周期的振幅与 TrailFactor 相乘得到动态跟踪距离。多单使用 买价 − 跟踪距离,空单使用 卖价 + 跟踪距离。当行情未盈利或调整幅度小于最小步长/冻结缓冲时,不会触发新的止损修改。
  • 止盈止损退出:一旦买价或卖价触及预存的止损或止盈价位,策略立即以市价单平仓。
  • 盘口数据:策略订阅订单簿以获取实时买一/卖一价格,从而复现原 MQ5 程序使用的 SymbolInfo.Bid/Ask 行为。

仓位规模与风险控制

  • 账户预留AccountReserve 指定部分权益不参与交易,对应 MQ5 输入参数 Inp_aed_AccountReserve
  • 订单额度:剩余可用资金再按 OrderReserve 限制单笔风险预算。
  • 风险检查
    • 如果预留资金小于分配额度(Equity × OrderReserve),则禁止开新仓。
    • 有效仓位大小按照 riskBudget / |entry - stop| 计算,并根据合约最小步长对齐。如果无法获取组合权益,则使用 BaseVolume 作为备用下单量。

参数

参数 说明
AccountReserve 预留不参与交易的权益比例(0–0.95)。
OrderReserve 参与交易资金中用于单笔风险预算的比例(0–1)。
PrimaryCandleType 用于突破判定的主周期(默认 15 分钟)。
SecondaryCandleType 用于跟踪止损的次级周期(默认 1 分钟)。
TakeFactor 主周期振幅的止盈倍数。
StopFactor 主周期振幅的止损倍数。
TrailFactor 次级周期振幅的跟踪止损倍数。
BaseVolume 在无法计算风险仓位时使用的基础下单量。
StopBufferTicks 额外的最小止损缓冲(以跳动单位表示)。
FreezeBufferTicks 防止频繁调整的冻结缓冲(以跳动单位表示)。
TrailStepMultiplier 设定最小跟踪步长的点差倍数。

使用说明

  • 需要同时提供主、次两个周期的 K 线以及订单簿数据,否则部分逻辑无法生效。
  • 由于信号依赖买卖报价,仅使用最新成交价进行回测可能与真实结果存在差异。
  • 策略启动时会自动启用仓位保护逻辑,与原 MQ5 程序的安全措施保持一致。
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// AIS2 Trading Robot strategy (simplified).
/// Breakout strategy using candle range with ATR filter.
/// Buys when close is near high of candle and ATR shows volatility.
/// Sells when close is near low of candle.
/// </summary>
public class Ais2TradingRobotStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _breakoutThreshold;

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

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	public decimal BreakoutThreshold
	{
		get => _breakoutThreshold.Value;
		set => _breakoutThreshold.Value = value;
	}

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

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period for volatility", "Indicators");

		_breakoutThreshold = Param(nameof(BreakoutThreshold), 0.85m)
			.SetDisplay("Breakout Threshold", "Candle body ratio threshold (0-1)", "Signals");
	}

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

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind((ICandleMessage candle) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var range = candle.HighPrice - candle.LowPrice;
				if (range <= 0)
					return;

				var bodyRatio = (candle.ClosePrice - candle.LowPrice) / range;

				// Buy on strong bullish candle (close near high)
				if (bodyRatio > BreakoutThreshold && candle.ClosePrice > candle.OpenPrice && Position <= 0)
				{
					BuyMarket();
				}
				// Sell on strong bearish candle (close near low)
				else if (bodyRatio < (1m - BreakoutThreshold) && candle.ClosePrice < candle.OpenPrice && Position >= 0)
				{
					SellMarket();
				}
			})
			.Start();

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