在 GitHub 上查看

Above Below MA 策略

Above Below MA 策略复刻了 MetaTrader 智能交易系统 Above Below MA (barabashkakvn's edition) 的思想。策略监控价格与可配置移动平均线之间的距离,当价格位于“错误”一侧且距离不小于设定阈值,同时均线自身朝着预期方向倾斜时才允许开仓。新版实现基于 StockSharp 高级 API,并且仅在蜡烛线收盘后做出决策。

概览

  • 市场环境:适用于价格经常回踩移动平均线后继续趋势的市场。
  • 标的:任意与 StockSharp 连接支持的品种,外汇品种尤为适合,因为原始版本以点(pip)衡量距离。
  • 周期:通过 Candle Type 参数设置(默认 1 分钟)。
  • 方向:支持多空双向,但同一时间仅持有一个净头寸。

策略逻辑

  1. 在选定的蜡烛序列上计算移动平均线。可以选择 SMA、EMA、SMMA、WMA 等平滑方法,以及 close、open、high、low、median、typical、weighted 等价格来源,并支持与 MetaTrader 相同的前移量。
  2. 将以点表示的最小距离利用标的的 PriceStep 转换为真实价格差。如果券商未提供价格步长,则自动跳过距离过滤。
  3. 每根收盘蜡烛执行以下检查:
    • 做多条件
      • 蜡烛开盘价与收盘价都要低于(并且至少低于最小距离)移位后的移动平均线。
      • 当前均线值高于上一根蜡烛的均线值(均线向上)。
    • 做空条件
      • 开盘价与收盘价都要高于(并且至少高于最小距离)移位后的移动平均线。
      • 当前均线值低于上一根蜡烛的均线值(均线向下)。
  4. 触发信号后,策略先平掉相反方向的仓位,再按信号方向发送市价单,确保不会同时持有多头与空头。

所有判断均基于收盘蜡烛,以避免在同一根未完成的蜡烛内重复进出。下单使用 BuyMarketSellMarket,数量由参数控制。

参数说明

参数 说明
MaPeriod 移动平均线周期,默认 6。
MaShift 均线前移的蜡烛数量。0 表示当前值,n 表示使用 n 根之前的值,默认 0。
MaMethod 均线类型:SimpleExponentialSmoothedWeighted,默认 Exponential
AppliedPrice 价格来源:close、open、high、low、median、typical、weighted,默认 Typical
MinimumDistancePips 价格与均线之间的最小距离(点),利用 PriceStep 转换为价格差,默认 5。
CandleType 用于计算的蜡烛类型,默认 1 分钟。
TradeVolume 新开仓的下单数量,默认 1。

其他说明

  • 策略内部未实现止损或止盈,请通过账户或外部模块控制风险。
  • 为了支持均线前移,仅保存所需数量的均线值,遵循“不创建多余集合”的约束。
  • PriceStep 不可用时,距离过滤会被跳过,进出场仅依据均线方向及价格位置。
  • 如果存在图表窗口,策略会自动绘制蜡烛、移动平均线以及成交记录。
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>
/// Above/Below MA strategy. Trades when price crosses above or below a moving average.
/// </summary>
public class AboveBelowMaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;

	private decimal? _prevClose;
	private decimal? _prevMa;

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

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

	public AboveBelowMaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

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

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

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

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

		_prevClose = null;
		_prevMa = null;

		var sma = new SimpleMovingAverage { Length = MaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, ProcessCandle)
			.Start();

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

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

		var close = candle.ClosePrice;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevClose = close;
			_prevMa = maVal;
			return;
		}

		if (_prevClose == null || _prevMa == null)
		{
			_prevClose = close;
			_prevMa = maVal;
			return;
		}

		// Price crosses above MA → buy
		if (_prevClose.Value <= _prevMa.Value && close > maVal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Price crosses below MA → sell
		else if (_prevClose.Value >= _prevMa.Value && close < maVal && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevMa = maVal;
	}
}