在 GitHub 上查看

Binario 策略

概述

Binario 是一套停损单突破系统,利用高价与低价移动平均线形成的包络来捕捉方向性扩张。价格运行在两条均线之间时,策略会在上下方同时挂出对称的止损买单与止损卖单,并为每个方向预先计算好止损与止盈距离。此实现基于 StockSharp 高阶 API,保留了原版 MetaTrader 5 EA 的主要思想,并通过 Level-1 行情估算买卖价差以匹配原始入场偏移。

交易逻辑

  1. 根据设定的周期与方法分别对蜡烛的最高价和最低价计算移动平均。
  2. 当最新收盘价位于两条均线之间时:
    • 在上方均线之上加上价差与 DifferencePips 后挂出买入止损单。
    • 在下方均线之下减去相同偏移后挂出卖出止损单。
  3. 每个挂单都会保存对应的止损与止盈价格,这些价格由均线数值、PointValue 与各个点数参数计算得出。
  4. 挂单成交后,策略会取消另一侧的挂单,并为当前持仓注册新的止损与止盈订单。
  5. 拖曳止损逻辑要求价格至少向有利方向移动 TrailingStopPips + TrailingStepPips 个点,然后将止损推进到 收盘价 ± TrailingStopPips,并且每次移动都必须超过前一次至少 TrailingStepPips 个点,与 MT5 中的步进式拖曳相一致。
  6. 当仓位从多头翻为空头或从空头翻为多头时,会先撤销旧的保护性订单以避免冲突。

参数说明

  • CandleType:用于计算的蜡烛周期。
  • MaPeriod:两条移动平均的长度。
  • MaShift:对均线输出应用的横向位移(单位:根数,0 为原始设定)。
  • HighMaMethod / LowMaMethod:高低均线的平滑方法,可选 SMAEMASMMAWMALWMA
  • PointValue:1 个点对应的绝对价格值(多数外汇货币对为 0.0001,日元相关品种为 0.01 等)。
  • DifferencePips:均线与挂单之间的额外点数缓冲。
  • TakeProfitPips:止盈距离(单位:点)。
  • TrailingStopPips:拖曳止损的基础距离,设为 0 可禁用拖曳。
  • TrailingStepPips:每次重新调整止损之前需要的额外盈利点数。
  • Volume:继承自 Strategy 的基础下单数量,反向挂单会自动加上当前仓位的绝对值以完成翻仓。

所有以点表示的参数都会通过 PointValue 转换为价格偏移,等价于 MT5 版本中的 Point * digits_adjust 计算方式。

委托管理

  • 仅在相应方向没有持仓时才会重新放置挂单(多头持仓时不再放置新的买入止损单,空头同理)。
  • 挂单成交后立即下达新的止损/止盈委托,并撤销未触发的对侧挂单。
  • 仓位翻转时会先撤掉旧的保护性委托,再根据新的方向重新下单,避免遗留订单。

拖曳止损

  • 多头:当价格相对入场上涨至少 TrailingStopPips + TrailingStepPips 点时,止损更新为 收盘价 - TrailingStopPips,并确保与上一档止损差距不少于 TrailingStepPips
  • 空头:当价格下跌同等幅度时,止损更新为 收盘价 + TrailingStopPips,同样满足步进条件。
  • 拖曳逻辑使用最新蜡烛的收盘价作为 MT5 PriceCurrent() 的替代。

数据需求

  • 选定周期的蜡烛数据。
  • Level-1 行情(最佳买价/卖价)用于计算实时价差;若暂时没有价差,策略会退回到品种最小价位步长或 PointValue

与 MT5 版本的差异

  • 仓位大小通过 StockSharp 的 Volume 属性控制,不再支持原版的 Lots/Risk 组合。
  • 由于 StockSharp 无法直接修改已有止损单,拖曳时会撤单并重新下单。
  • 成交价以挂单记录的价格作为近似值;必要时请调整 PointValue 与点数参数以贴合券商报价。
  • 策略仅在蜡烛收盘时运行,相当于在 MT5 中启用「每根新柱执行一次」。

使用建议

  1. 根据交易品种设置正确的 PointValue
  2. 调整移动平均方法与周期以复现原有模板。
  3. 为差值、止盈与拖曳参数选择合适的点数。
  4. 确保订阅 Level-1 数据,以便正确计算入场所需的价差。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Binario strategy (simplified). Uses two EMAs as channel and trades breakouts.
/// </summary>
public class BinarioStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;

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

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

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

		_maPeriod = Param(nameof(MaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Moving average length", "Indicators");
	}

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

		var highest = new Highest { Length = MaPeriod };
		var lowest = new Lowest { Length = MaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, (ICandleMessage candle, decimal highValue, decimal lowValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;
				var mid = (highValue + lowValue) / 2m;
				var channelPadding = (highValue - lowValue) * 0.1m;

				// Buy on breakout above channel midpoint
				if (close > mid + channelPadding && Position <= 0)
				{
					BuyMarket();
				}
				// Sell on breakout below channel midpoint
				else if (close < mid - channelPadding && Position >= 0)
				{
					SellMarket();
				}
			})
			.Start();

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