在 GitHub 上查看
Binario 策略
概述
Binario 是一套停损单突破系统,利用高价与低价移动平均线形成的包络来捕捉方向性扩张。价格运行在两条均线之间时,策略会在上下方同时挂出对称的止损买单与止损卖单,并为每个方向预先计算好止损与止盈距离。此实现基于 StockSharp 高阶 API,保留了原版 MetaTrader 5 EA 的主要思想,并通过 Level-1 行情估算买卖价差以匹配原始入场偏移。
交易逻辑
- 根据设定的周期与方法分别对蜡烛的最高价和最低价计算移动平均。
- 当最新收盘价位于两条均线之间时:
- 在上方均线之上加上价差与
DifferencePips 后挂出买入止损单。
- 在下方均线之下减去相同偏移后挂出卖出止损单。
- 每个挂单都会保存对应的止损与止盈价格,这些价格由均线数值、
PointValue 与各个点数参数计算得出。
- 挂单成交后,策略会取消另一侧的挂单,并为当前持仓注册新的止损与止盈订单。
- 拖曳止损逻辑要求价格至少向有利方向移动
TrailingStopPips + TrailingStepPips 个点,然后将止损推进到 收盘价 ± TrailingStopPips,并且每次移动都必须超过前一次至少 TrailingStepPips 个点,与 MT5 中的步进式拖曳相一致。
- 当仓位从多头翻为空头或从空头翻为多头时,会先撤销旧的保护性订单以避免冲突。
参数说明
CandleType:用于计算的蜡烛周期。
MaPeriod:两条移动平均的长度。
MaShift:对均线输出应用的横向位移(单位:根数,0 为原始设定)。
HighMaMethod / LowMaMethod:高低均线的平滑方法,可选 SMA、EMA、SMMA、WMA、LWMA。
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 中启用「每根新柱执行一次」。
使用建议
- 根据交易品种设置正确的
PointValue。
- 调整移动平均方法与周期以复现原有模板。
- 为差值、止盈与拖曳参数选择合适的点数。
- 确保订阅 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);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class binario_strategy(Strategy):
def __init__(self):
super(binario_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ma_period = self.Param("MaPeriod", 20) \
.SetDisplay("MA Period", "Moving average length", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaPeriod(self):
return self._ma_period.Value
def OnStarted2(self, time):
super(binario_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self.MaPeriod
lowest = Lowest()
lowest.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(highest, lowest, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
close = float(candle.ClosePrice)
mid = (hv + lv) / 2.0
channel_padding = (hv - lv) * 0.1
if close > mid + channel_padding and self.Position <= 0:
self.BuyMarket()
elif close < mid - channel_padding and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return binario_strategy()