在 GitHub 上查看

ValidateMe 策略

概述

ValidateMe 策略移植自原始的 MQL4 专家顾问,核心用途是执行下单前的基本校验。它会确认止损与止盈距离是否符合交易所限制、是否有足够资金,并在满足条件时按照预设方向发出一笔市价单。策略持续监听成交数据,仅在没有仓位且不存在活动订单时才会重新进场。

交易逻辑

  1. 订阅所选标的的逐笔成交数据。
  2. 当策略状态正常(数据形成、在线且允许交易)并且没有持仓和活动订单时,才允许发送新订单。
  3. 根据参数指定的方向(买入或卖出)及手数提交市价单。
  4. 使用由点差(pip)转换得到的绝对价格偏移,立即挂出对应的止盈和止损保护单,并考虑到 3/5 位小数品种的点值换算。
  5. 当仓位平仓后,策略在下一笔成交到来时重复检查条件,并在满足时再次入场。

参数

参数 说明
Take Profit (pips) 止盈距离,单位为点(pip),必须大于 0。
Stop Loss (pips) 止损距离,单位为点(pip),必须大于 0。
Lots 每次市价单使用的交易手数。
Direction 下单方向,可选买入或卖出。

风险管理

  • 通过 StartProtection 设置绝对价格偏移,实现止盈与止损保护。
  • 根据证券的最小报价步长与小数位数计算点值,使其与 MetaTrader 的点值逻辑保持一致(对 3/5 位小数进行十倍放大)。
  • 仅在不存在活动订单时才会触发新的交易,避免堆叠订单。

使用建议

  • 运行前先设置好交易手数、止盈和止损点差以及方向。
  • 策略不依赖任何指标,更适合作为风控与执行框架的示例。
  • 如需额外的资金曲线或回撤限制,可在组合层面结合其他风控组件实现。
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// ValidateMe strategy: RSI crossover at oversold/overbought levels.
/// Buys when RSI crosses above 30, sells when RSI crosses below 70.
/// </summary>
public class ValidateMeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;

	private decimal _prevRsi;
	private bool _hasPrev;

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

	public ValidateMeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_period = Param(nameof(Period), 14)
			.SetGreaterThanZero()
			.SetDisplay("Period", "RSI period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var rsi = new RelativeStrengthIndex { Length = Period };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevRsi < 30 && rsiValue >= 30 && Position <= 0)
				BuyMarket();
			else if (_prevRsi > 70 && rsiValue <= 70 && Position >= 0)
				SellMarket();
		}

		_prevRsi = rsiValue;
		_hasPrev = true;
	}
}