在 GitHub 上查看

eInTradePanel 策略

eInTradePanel 策略把 MetaTrader 版本的交易面板自动化。它支持八种下单方式(买入/卖出、止损、限价、止损限价),并根据当前点差和 55 周期 ATR 计算触发价、挂单价、止损和止盈。通过监控收盘 K 线来模拟保护单,因此即使经纪商不支持附带 SL/TP,也能正确处理风控。

特点

  • 多种下单模式:可选择买入、卖出、买/卖止损、买/卖限价、买/卖止损限价。止损限价在触发价被突破后再挂出限价单。
  • 动态距离:挂单、触发、止损、止盈都基于当前点差和 ATR × AtrFactor 的合成点差中较大的数值。当 ATR 尚未形成时使用可配置的基础 tick 距离。
  • 波动自适应:ATR 长度固定为 55,完全继承原面板的响应速度。
  • 挂单过期:可选的失效时间(默认至少 11 分钟)自动取消陈旧挂单。
  • 风控监控:每根收盘 K 线检查是否突破止损或止盈,一旦命中立即市价平仓。
  • 行情感知:订阅盘口以获取最佳买卖价,若无盘口数据则退回到 K 线收盘价。

参数

名称 说明
Volume 下单数量。
Mode 下单模式(市价、止损、限价、止损限价)。
Candle Type 用于 ATR 与信号计算的 K 线类型。
Base Ticks ATR 不可用时的最小 tick 距离。
Pending Multiplier 挂单距离的倍数。
Trigger Multiplier 止损限价触发距离的倍数。
Stop Multiplier 止损距离的倍数(0 表示关闭)。
Take Multiplier 止盈距离的倍数(0 表示关闭)。
Use ATR 是否启用基于 ATR 的动态缩放。
ATR Factor ATR 中用于模拟点差的比例。
Expiration 挂单在多少分钟后取消(0 表示 GTC)。
Min Expiration 挂单最短存活时间,防止过早取消。

运行流程

  1. 数据准备:订阅设定的 K 线并维护 ATR(55),同时记录盘口最佳买卖价。
  2. 距离计算:每根完结的 K 线重新计算基础 tick 距离,并据此得到挂单、触发、止损、止盈价格。
  3. 下单逻辑
    • 市价模式在策略空仓时立即执行。
    • 止损与限价模式发送挂单,可在到期后自动撤销。
    • 止损限价模式等待触发价被高/低点突破后再挂出限价。
  4. 持仓管理:持仓期间若收盘 K 线触及止损或止盈,则市价平仓。
  5. 状态重置:当无持仓且无活动挂单时,重新计算价格准备下一笔交易。

该方案在 StockSharp 高级 API 内复刻了手动面板的全部逻辑,适合需要自动化执行的盘面交易者。

using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EInTradePanel strategy. Uses ATR breakout for entries.
/// </summary>
public class EInTradePanelStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _multiplier;
	private decimal? _prevClose;
	private decimal? _prevAtr;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal Multiplier { get => _multiplier.Value; set => _multiplier.Value = value; }

	public EInTradePanelStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_atrPeriod = Param(nameof(AtrPeriod), 14).SetGreaterThanZero().SetDisplay("ATR Period", "ATR lookback", "Indicators");
		_multiplier = Param(nameof(Multiplier), 1.5m).SetDisplay("Multiplier", "ATR multiplier for breakout", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = null;
		_prevAtr = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = null; _prevAtr = null;
		var atr = new AverageTrueRange { Length = AtrPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, atr); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevClose = candle.ClosePrice; _prevAtr = atrVal; return; }
		if (_prevClose == null || _prevAtr == null) { _prevClose = candle.ClosePrice; _prevAtr = atrVal; return; }
		var threshold = _prevAtr.Value * Multiplier;
		var diff = candle.ClosePrice - _prevClose.Value;
		if (diff > threshold && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (diff < -threshold && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevClose = candle.ClosePrice; _prevAtr = atrVal;
	}
}