在 GitHub 上查看

MA包络线策略

策略由MetaTrader 5专家顾问“MA Envelopes”移植而来。它利用均线与包络线构建的通道寻找价格回踩机会。当完成的K线收盘价位于均线与包络上/下轨之间,并且处于设定的交易时间窗口内时,策略会在均线位置挂出限价单,同时设置基于包络线的止损和止盈。

交易逻辑

  1. 根据选择的均线类型、价格源和周期计算移动平均值,并利用偏差参数构建对称的包络线。
  2. 若完成K线收盘价高于均线且低于上轨,且当前卖价仍高于均线,则按阶梯方式在均线处挂出买入限价单。
    • 每个买单的止损设置在下轨,止盈设置在上轨基础上再加对应的点数偏移。
    • 最多管理三张独立的订单,对应 FirstSecondThird 三组止盈偏移参数。
  3. 若完成K线收盘价低于均线且高于下轨,且当前买价仍低于均线,则镜像执行卖出限价逻辑。
  4. StartHourEndHour 控制交易时间。超过结束时间仍未成交的挂单会被取消。
  5. MaximumRisk 控制单笔风险占权益的比例,DecreaseFactor 在连续亏损后按比例减少下次下单手数。下单数量会按照品种的成交量步长和最小/最大限制自动调整。
  6. 当挂单完全成交后,立即注册止损和止盈委托;若其中一张触发,另一张会被取消。如仅部分仓位被平掉,则为剩余仓位重新下达防护订单。

参数说明

参数 说明
MaximumRisk 单笔交易所冒风险占可用权益的比例。
DecreaseFactor 连续亏损时用于缩减下次下单手数的系数。
First/Second/ThirdStopTakeProfitPips 三组阶梯订单在包络线基础上的附加点数。
StartHour, EndHour 交易窗口的起止小时(终端时间,0–23)。
MaPeriod, MaShift, MaMethodType, AppliedPrice 均线配置。
EnvelopeDeviation 包络线宽度,单位为百分比。
CandleType 策略处理的K线周期。

其他说明

  • 当仓位仅部分被止损或止盈时,策略会重新为剩余仓位生成新的防护订单。
  • 仅在交易窗口结束时取消未成交的挂单,已有仓位继续由止损止盈保护。
  • 策略优先使用盘口数据获取最新买卖价;若无盘口则回退到K线收盘价。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class MaEnvelopesStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;
	private decimal? _prevHigh, _prevLow;

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

	public MaEnvelopesStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_period = Param(nameof(Period), 15).SetGreaterThanZero().SetDisplay("Channel Period", "Channel lookback", "Indicators");
	}

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

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevHigh = null; _prevLow = null;
		var highest = new Highest { Length = Period };
		var lowest = new Lowest { Length = Period };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevHigh = high; _prevLow = low; return; }
		if (_prevHigh == null || _prevLow == null) { _prevHigh = high; _prevLow = low; return; }
		if (candle.ClosePrice > _prevHigh.Value && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (candle.ClosePrice < _prevLow.Value && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevHigh = high; _prevLow = low;
	}
}