在 GitHub 上查看

Wlx BW5 区域策略

本策略利用比尔·威廉姆斯的Awesome振荡器 (AO) 和Accelerator振荡器 (AC),当两者连续五根柱子同向移动时生成信号。当AO和AC连续上升五根时做多;连续下降五根时做空。收到相反信号时反向持仓。

详情

  • 入场条件
    • 多头AOAC 连续五根上升。
    • 空头AOAC 连续五根下降。
  • 多空方向:双向。
  • 出场条件:出现反向信号时反手。
  • 止损:无。
  • 默认值
    • 时间框架 = 4 小时。
    • Direct = true。
    • SignalBar = 1。
  • 筛选
    • 分类:动量
    • 方向:双向
    • 指标:多个
    • 止损:无
    • 复杂度:中等
    • 时间框架:中期
    • 季节性:否
    • 神经网络:否
    • 背离:否
    • 风险等级:中等
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>
/// Bill Williams zone strategy based on Awesome and Accelerator oscillators.
/// Opens or reverses positions after five consecutive oscillator moves.
/// </summary>
public class WlxBw5ZoneStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<bool> _direct;
	private readonly StrategyParam<int> _signalBar;

	private AwesomeOscillator _ao;
	private SimpleMovingAverage _aoSma;

	private decimal? _ao0, _ao1, _ao2, _ao3, _ao4;
	private decimal? _ac0, _ac1, _ac2, _ac3, _ac4;
	private bool _flagUp;
	private bool _flagDown;

	/// <summary>
	/// Candle type used by the strategy.
	/// </summary>
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	/// <summary>
	/// Direction flag. If false signals are reversed.
	/// </summary>
	public bool Direct { get => _direct.Value; set => _direct.Value = value; }

	/// <summary>
	/// Signal bar shift.
	/// </summary>
	public int SignalBar { get => _signalBar.Value; set => _signalBar.Value = value; }

	/// <summary>
	/// Initializes a new instance of <see cref="WlxBw5ZoneStrategy"/>.
	/// </summary>
	public WlxBw5ZoneStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");

		_direct = Param(nameof(Direct), true)
			.SetDisplay("Direct", "Use direct signals", "General");

		_signalBar = Param(nameof(SignalBar), 1)
			.SetRange(0, 5)
			.SetDisplay("Signal Bar", "Bar shift for signals", "General");
	}

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

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

		_ao = null;
		_aoSma = null;
		_ao0 = _ao1 = _ao2 = _ao3 = _ao4 = null;
		_ac0 = _ac1 = _ac2 = _ac3 = _ac4 = null;
		_flagUp = false;
		_flagDown = false;
	}

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

		_ao = new AwesomeOscillator();
		_aoSma = new SimpleMovingAverage { Length = 5 };
		_ao0 = _ao1 = _ao2 = _ao3 = _ao4 = null;
		_ac0 = _ac1 = _ac2 = _ac3 = _ac4 = null;
		_flagUp = false;
		_flagDown = false;

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

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

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

		var ac = ao - _aoSma.Process(new DecimalIndicatorValue(_aoSma, ao, candle.CloseTime) { IsFinal = true }).ToDecimal();

		_ao4 = _ao3;
		_ao3 = _ao2;
		_ao2 = _ao1;
		_ao1 = _ao0;
		_ao0 = ao;
		_ac4 = _ac3;
		_ac3 = _ac2;
		_ac2 = _ac1;
		_ac1 = _ac0;
		_ac0 = ac;

		if (_ao4 is null || _ac4 is null)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var isUpSeq = _ao0 > _ao1 && _ao1 > _ao2 && _ao2 > _ao3 && _ao3 > _ao4 &&
			_ac0 > _ac1 && _ac1 > _ac2 && _ac2 > _ac3 && _ac3 > _ac4;

		var isDownSeq = _ao0 < _ao1 && _ao1 < _ao2 && _ao2 < _ao3 && _ao3 < _ao4 &&
			_ac0 < _ac1 && _ac1 < _ac2 && _ac2 < _ac3 && _ac3 < _ac4;

		if (!_flagUp && isUpSeq)
		{
			if (Direct)
			{
				if (Position <= 0)
					BuyMarket(Position < 0 ? Volume + Math.Abs(Position) : Volume);
			}
			else
			{
				if (Position >= 0)
					SellMarket(Position > 0 ? Volume + Position : Volume);
			}

			_flagUp = true;
		}

		if (!_flagDown && isDownSeq)
		{
			if (Direct)
			{
				if (Position >= 0)
					SellMarket(Position > 0 ? Volume + Position : Volume);
			}
			else
			{
				if (Position <= 0)
					BuyMarket(Position < 0 ? Volume + Math.Abs(Position) : Volume);
			}

			_flagDown = true;
		}

		if (_ao0 <= _ao1 || _ac0 <= _ac1)
			_flagUp = false;

		if (_ao0 >= _ao1 || _ac0 >= _ac1)
			_flagDown = false;
	}
}