在 GitHub 上查看

ScalpWiz Bollinger 策略

概述

ScalpWiz Bollinger 是一款反趋势策略,利用布林带判断价格过度延伸。当收盘价远离上轨或下轨时,策略将在相反方向开仓,期待价格回归。

策略检查四个距离级别,每个级别代表不同的信号强度并相应地放大交易量。仓位大小还根据当前投资组合价值的风险百分比进行调整。

参数

  • BandsPeriod – 计算布林带所使用的蜡烛数量。
  • BandsDeviation – 布林带的标准差倍数。
  • Level1PipsLevel4Pips – 触发各级别信号所需的点数距离。
  • StrengthLevel1MultiplierStrengthLevel4Multiplier – 各级别的交易量倍数。
  • RiskPercent – 每个信号所承担的账户风险百分比。
  • CandleType – 用于计算的蜡烛周期。

交易逻辑

  1. 订阅所选周期的蜡烛并计算布林带。
  2. 每根完成的蜡烛:
    • 如果收盘价高于上轨一定距离,则开空单;
    • 如果收盘价低于下轨一定距离,则开多单。
  3. 交易量根据风险百分比和信号强度倍数计算。

该策略灵感来自原始的 MQL 脚本 mcb.scalpwiz.9001.mq4

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bollinger-based reverse scalping strategy.
/// Places counter-trend orders when price deviates from the bands.
/// Position size scales with signal strength and risk percentage.
/// </summary>
public class ScalpWizBollingerStrategy : Strategy
{
	private readonly StrategyParam<int> _bandsPeriod;
	private readonly StrategyParam<decimal> _bandsDeviation;
	private readonly StrategyParam<decimal> _level1Pips;
	private readonly StrategyParam<decimal> _level2Pips;
	private readonly StrategyParam<decimal> _level3Pips;
	private readonly StrategyParam<decimal> _level4Pips;
	private readonly StrategyParam<int> _multiplier1;
	private readonly StrategyParam<int> _multiplier2;
	private readonly StrategyParam<int> _multiplier3;
	private readonly StrategyParam<int> _multiplier4;
	private readonly StrategyParam<int> _riskPercent;
	private readonly StrategyParam<DataType> _candleType;
	
	public int BandsPeriod { get => _bandsPeriod.Value; set => _bandsPeriod.Value = value; }
	public decimal BandsDeviation { get => _bandsDeviation.Value; set => _bandsDeviation.Value = value; }
	public decimal Level1Pips { get => _level1Pips.Value; set => _level1Pips.Value = value; }
	public decimal Level2Pips { get => _level2Pips.Value; set => _level2Pips.Value = value; }
	public decimal Level3Pips { get => _level3Pips.Value; set => _level3Pips.Value = value; }
	public decimal Level4Pips { get => _level4Pips.Value; set => _level4Pips.Value = value; }
	public int StrengthLevel1Multiplier { get => _multiplier1.Value; set => _multiplier1.Value = value; }
	public int StrengthLevel2Multiplier { get => _multiplier2.Value; set => _multiplier2.Value = value; }
	public int StrengthLevel3Multiplier { get => _multiplier3.Value; set => _multiplier3.Value = value; }
	public int StrengthLevel4Multiplier { get => _multiplier4.Value; set => _multiplier4.Value = value; }
	public int RiskPercent { get => _riskPercent.Value; set => _riskPercent.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	
	/// <summary>
	/// Constructor.
	/// </summary>
	public ScalpWizBollingerStrategy()
	{
		_bandsPeriod = Param(nameof(BandsPeriod), 30)
		.SetGreaterThanZero()
		.SetDisplay("Bands Period", "Number of candles for Bollinger calculation", "Bollinger")
		
		.SetOptimize(20, 40, 5);
		
		_bandsDeviation = Param(nameof(BandsDeviation), 2m)
		.SetGreaterThanZero()
		.SetDisplay("Bands Deviation", "Standard deviation multiplier", "Bollinger")
		
		.SetOptimize(1m, 3m, 0.5m);
		
		_level1Pips = Param(nameof(Level1Pips), 1m)
		.SetGreaterThanZero()
		.SetDisplay("Level1 Pips", "Deviation from band for weakest signal", "Levels");

		_level2Pips = Param(nameof(Level2Pips), 5m)
		.SetGreaterThanZero()
		.SetDisplay("Level2 Pips", "Deviation from band for level 2", "Levels");

		_level3Pips = Param(nameof(Level3Pips), 10m)
		.SetGreaterThanZero()
		.SetDisplay("Level3 Pips", "Deviation from band for level 3", "Levels");

		_level4Pips = Param(nameof(Level4Pips), 20m)
		.SetGreaterThanZero()
		.SetDisplay("Level4 Pips", "Deviation from band for strongest signal", "Levels");
		
		_multiplier1 = Param(nameof(StrengthLevel1Multiplier), 1)
		.SetGreaterThanZero()
		.SetDisplay("Strength 1 Multiplier", "Volume multiplier for level 1", "Strength");
		
		_multiplier2 = Param(nameof(StrengthLevel2Multiplier), 2)
		.SetGreaterThanZero()
		.SetDisplay("Strength 2 Multiplier", "Volume multiplier for level 2", "Strength");
		
		_multiplier3 = Param(nameof(StrengthLevel3Multiplier), 3)
		.SetGreaterThanZero()
		.SetDisplay("Strength 3 Multiplier", "Volume multiplier for level 3", "Strength");
		
		_multiplier4 = Param(nameof(StrengthLevel4Multiplier), 4)
		.SetGreaterThanZero()
		.SetDisplay("Strength 4 Multiplier", "Volume multiplier for level 4", "Strength");
		
		_riskPercent = Param(nameof(RiskPercent), 2)
		.SetGreaterThanZero()
		.SetDisplay("Risk %", "Risk percentage per trade", "General");
		
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles to use", "General");
	}
	
	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}
	
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		
		var bollinger = new BollingerBands
		{
			Length = BandsPeriod,
			Width = BandsDeviation
		};
		
		var subscription = SubscribeCandles(CandleType);
		
		subscription
		.BindEx(bollinger, ProcessCandle)
		.Start();
	}
	
	private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var bb = (BollingerBandsValue)value;
		
		if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
		return;
		
		var step = Security?.PriceStep ?? 0.0001m;
		var close = candle.ClosePrice;
		
		if (close - upper > Level4Pips * step)
		{
			SellByStrength(StrengthLevel4Multiplier, close);
		}
		else if (close - upper > Level3Pips * step)
		{
			SellByStrength(StrengthLevel3Multiplier, close);
		}
		else if (close - upper > Level2Pips * step)
		{
			SellByStrength(StrengthLevel2Multiplier, close);
		}
		else if (close - upper > Level1Pips * step)
		{
			SellByStrength(StrengthLevel1Multiplier, close);
		}
		else if (lower - close > Level4Pips * step)
		{
			BuyByStrength(StrengthLevel4Multiplier, close);
		}
		else if (lower - close > Level3Pips * step)
		{
			BuyByStrength(StrengthLevel3Multiplier, close);
		}
		else if (lower - close > Level2Pips * step)
		{
			BuyByStrength(StrengthLevel2Multiplier, close);
		}
		else if (lower - close > Level1Pips * step)
		{
			BuyByStrength(StrengthLevel1Multiplier, close);
		}
	}
	
	private void BuyByStrength(int strength, decimal price)
	{
		BuyMarket();
	}

	private void SellByStrength(int strength, decimal price)
	{
		SellMarket();
	}
}