在 GitHub 上查看

Spazm 波动性突破策略

摘要

  • 将 MetaTrader 4 智能交易系统 Spazm (8683) 转换到 StockSharp 高层 API。
  • 通过比较收盘价与由波动性决定的上下包络带来捕捉突破,同时跟踪最近的摆动高点和低点。
  • 可选地在图表上绘制连接多头与空头枢轴点的线段,重现原始 MQL 指标的可视化效果。

数据准备流程

  1. 根据参数 CandleType 订阅目标证券的蜡烛图序列。
  2. 每根已完成的蜡烛提供一个原始波动样本:
    • 默认情况下样本等于 High - Low
    • 启用 UseOpenCloseRange 时,改用实体大小 |Open - Close|
  3. 样本转换为价格步长数量(通过 PriceStep),从而消除不同品种的价格刻度差异。
  4. 依据 UseWeightedVolatility 选择合适的平滑指标:
    • false → 使用长度为 VolatilityPeriod 的简单移动平均。
    • true → 使用同长度的线性加权移动平均以强调最新数据。
  5. 平滑后的范围(以步长计)乘以 VolatilityMultiplier 并换算回价格单位,得到自适应的突破阈值。
  6. 在预热阶段(前 VolatilityPeriod * 3 根蜡烛)策略持续更新最近的最高价、最低价及其时间戳,并依据二者孰新来确定初始趋势方向。

参数

参数 默认值 说明
Volume 1 每次开仓或反手时提交的市场订单数量。
VolatilityMultiplier 5 对平均波动范围的放大倍数,用于构建突破通道。
VolatilityPeriod 24 波动性估计与初始摆动扫描所需的蜡烛数量。
UseWeightedVolatility false 是否改用线性加权移动平均。
UseOpenCloseRange false 是否使用开收盘差值代替高低价差。
StopLossMultiplier 0 以突破阈值为基准计算止损距离的倍数,至少为三个价格步长;设为 0 时禁用止损。
DrawSwingLines true 是否绘制连接最近多空枢轴点的线段。
CandleType 4 小时时间框架 用于计算的蜡烛类型或时间框架。

交易逻辑

  1. 初始化阶段
    • 在前 VolatilityPeriod * 3 根蜡烛内持续更新 _highestPrice_lowestPrice 以及对应时间。
    • 当样本数量达到要求时,根据哪个极值更靠近当前时间来决定初始趋势:最近低点 → 看多;最近高点 → 看空。
    • 同时把这些极值保存为首个枢轴点,以便开始绘制趋势线。
  2. 波动性估计
    • 每根完成蜡烛将其范围送入所选的移动平均,生成当前阈值。
    • 阈值至少为一个价格步长,避免出现零带宽。
  3. 摆动维护
    • 无论趋势方向如何,新的绝对高点或低点都会刷新存储的摆动极值。
    • 当趋势翻转时,将对应极值记录为枢轴点,并在启用可视化时与对向枢轴连线。
  4. 突破条件
    • 看多阶段:收盘价跌破 _highestPrice - threshold 时,以 Volume + |Position| 的数量卖出,从而一次性反手至净空头。
    • 看空阶段:收盘价突破 _lowestPrice + threshold 时,对称地反手为多头。
  5. 止损管理
    • StopLossMultiplier > 0,则依据 threshold * StopLossMultiplier(至少三个步长)计算出虚拟止损价位。
    • 当蜡烛的最低价跌破多头止损,或最高价突破空头止损,即刻以市价单平仓。
  6. 基础设施
    • 调用 StartProtection() 立即开启 StockSharp 的安全保护机制。
    • 所有决策均基于已完成的蜡烛,符合原始 EA 的“按柱更新”模式。

与 MQL 版本的差异

  • 原脚本按 tick 级别运行,本移植版本使用完成的蜡烛数据,这是 StockSharp 高层 API 的推荐方式。
  • 无法获取经纪商的最小止损距离(如 MODE_STOPLEVEL),因此策略对止损偏移采用三步长的保守下限。
  • 反手操作通过一次 BuyMarket / SellMarket 调用同时完成平仓与开仓,而非逐单遍历。
  • 图表呈现依赖 DrawLine 方法,但连接顺序与 MQL 中的趋势线保持一致。

使用建议

  • 确认标的证券提供正确的 PriceStep。若缺失,代码将回退到 1,可能需要手动调整。
  • 时间框架越小,波动性均值越容易受噪声影响。建议采用接近原 EA 的 H4 周期。
  • StopLossMultiplier 保持为零可复制原策略的无止损行为;提高该值可引入风险控制。
  • 策略不设定固定止盈目标,仅通过趋势反转或止损触发离场。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Volatility breakout strategy that tracks swing extremes and reverses
/// when price breaks beyond an ATR-based volatility band.
/// </summary>
public class SpazmVolatilityBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _multiplier;

	private decimal _swingHigh;
	private decimal _swingLow;
	private bool _trendUp;
	private bool _initialized;

	public SpazmVolatilityBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "Period for ATR volatility.", "Indicators");

		_multiplier = Param(nameof(Multiplier), 2.0m)
			.SetDisplay("Multiplier", "ATR multiplier for breakout threshold.", "Indicators");
	}

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

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal Multiplier
	{
		get => _multiplier.Value;
		set => _multiplier.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_swingHigh = 0;
		_swingLow = decimal.MaxValue;
		_trendUp = true;
		_initialized = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var atr = new AverageTrueRange { Length = AtrLength };

		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 atrValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (atrValue <= 0)
			return;

		var close = candle.ClosePrice;
		var high = candle.HighPrice;
		var low = candle.LowPrice;
		var threshold = atrValue * Multiplier;

		if (!_initialized)
		{
			_swingHigh = high;
			_swingLow = low;
			_initialized = true;
			return;
		}

		if (_trendUp)
		{
			// Track swing high
			if (high > _swingHigh)
				_swingHigh = high;

			// Reversal: price breaks below swing high minus threshold
			if (close < _swingHigh - threshold)
			{
				_trendUp = false;
				_swingLow = low;

				// Enter short on trend reversal
				if (Position > 0)
					SellMarket();
				if (Position == 0)
					SellMarket();
			}
		}
		else
		{
			// Track swing low
			if (low < _swingLow)
				_swingLow = low;

			// Reversal: price breaks above swing low plus threshold
			if (close > _swingLow + threshold)
			{
				_trendUp = true;
				_swingHigh = high;

				// Enter long on trend reversal
				if (Position < 0)
					BuyMarket();
				if (Position == 0)
					BuyMarket();
			}
		}
	}
}