在 GitHub 上查看

加密货币背离策略

概述

加密货币背离策略 利用价格与相对强弱指标(RSI)之间的传统动量背离,同时用快慢均线和 MACD 验证趋势方向。原始 MetaTrader 智能交易系统依赖多周期动量、资金管理以及复杂的拖尾处理。本移植版本保留了策略精神:

  • 当价格创出更低低点而 RSI 创出更高低点时识别看涨背离。
  • 当价格创出更高高点而 RSI 创出更低高点时识别看跌背离。
  • 通过快/慢均线与 MACD 线相对信号线的位置确认信号。
  • 通过以价格最小变动单位表示的止损、止盈、保本和追踪止损管理仓位。

该策略专为波动性充足的现货加密资产设计,也可应用于其它具备明显波动的标的。

指标

  • 简单移动平均线(SMA):快线与慢线提供主要趋势过滤。
  • 相对强弱指标(RSI):记录动量拐点,用于衡量背离力度。
  • 移动平均收敛发散(MACD):确认动量与背离方向一致。

所有指标都通过高级 API 绑定,无需手动维护缓存。

交易逻辑

  1. 订阅配置的 K 线类型,并在每根收盘 K 线上计算 SMA、RSI 与 MACD。
  2. 追踪最近的摆动高点和低点及其对应的 RSI 数值,仅当价格创出新高/新低时更新数据。
  3. 看涨背离:价格形成新低而 RSI 低点上升,同时要求快 SMA 高于慢 SMA、MACD 线高于信号线且 RSI 低于看涨阈值(默认 45),以确认超卖环境。
  4. 看跌背离:价格形成新高而 RSI 高点下降,同时要求快 SMA 低于慢 SMA、MACD 线低于信号线且 RSI 高于看跌阈值(默认 55)。
  5. 策略一次仅持有一个净仓位。当反向条件成立时,会先平掉现有仓位再立即进场反向交易。

风险控制

  • 交易量:用户自定义的下单数量。
  • 止损 / 止盈:以价格最小变动单位表示,根据实际成交价自动设置。
  • 保本移动:价格运行达到设定距离后,可将止损上移/下移至入场价附近的偏移位置。
  • 追踪止损:按照固定距离紧跟收盘价,激活后优先于初始止损生效。

所有保护机制在每根收盘 K 线执行,可确保回测与实时表现一致。

参数

名称 说明
CandleType 分析所使用的 K 线类型(默认 15 分钟)。
TradeVolume 每次进场使用的下单数量。
FastMaLength / SlowMaLength 快、慢 SMA 的周期。
RsiLength RSI 的计算周期。
RsiBullishLevel / RsiBearishLevel 用于确认背离的 RSI 超卖与超买阈值。
MacdShortLength / MacdLongLength / MacdSignalLength MACD 的参数设置。
StopLossPoints / TakeProfitPoints 以价格最小变动单位表示的止损与止盈距离。
EnableBreakEven, BreakEvenTrigger, BreakEvenOffset 保本移动的开关与触发设置。
EnableTrailing, TrailDistance 追踪止损的开关与距离。

所有参数均通过 StrategyParam<T> 暴露,可在 StockSharp 设计器中优化。

使用提示

  1. 将策略附加到目标加密资产,确保品种定义了 PriceStepBoard,否则无法计算保护价位。
  2. 根据市场特性调整 K 线周期(例如 15 分钟或 1 小时),背离识别对周期非常敏感。
  3. 根据波动性调整止损与止盈距离。常见的 5 位小数报价货币对通常需要更大的步长设置。
  4. 只有在历史测试显示足够浮盈空间后再启用保本或追踪功能,过早移动可能导致提前离场。
  5. 在 StockSharp 设计器或行情面板中观察指标对齐情况与成交记录,便于调试。

与 MQL 版本的差异

  • 将以金额计算的拖尾和权益止损简化为基于价格步长的统一止损管理。
  • 多周期动量过滤改为单周期 MACD 确认,提高可读性。
  • 省略邮件与通知功能,可在 StockSharp 生态中的其它组件实现。

尽管做出上述调整,本策略仍忠实呈现原始智能交易系统的背离思想与保护机制。

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;

public class CryptocurrencyDivergenceStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public CryptocurrencyDivergenceStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}