在 GitHub 上查看

Color Laguerre

基于 Color Laguerre 振荡器的趋势策略。

Color Laguerre 使用拉盖尔滤波器平滑价格序列,并通过颜色变化标记趋势方向。当振荡器转为看涨时策略买入,转为看跌时卖出。达到极端水平时,在动能减弱下可强制平仓。

详情

  • 入场条件: 振荡器穿越中线。
  • 多空方向: 双向。
  • 退出条件: 反向信号或止损。
  • 止损: 是。
  • 默认值:
    • Gamma = 0.7m
    • HighLevel = 85
    • MiddleLevel = 50
    • LowLevel = 15
    • StopLossPercent = 2m
    • CandleType = TimeSpan.FromHours(1)
  • 过滤器:
    • 类型: 趋势
    • 方向: 双向
    • 指标: 振荡器
    • 止损: 是
    • 复杂度: 基础
    • 时间框架: 日内 (1h)
    • 季节性: 无
    • 神经网络: 无
    • 背离: 无
    • 风险等级: 中等
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>
/// Trend-following strategy using Color Laguerre oscillator.
/// </summary>
public class ColorLaguerreStrategy : Strategy
{
	private readonly StrategyParam<decimal> _gamma;
	private readonly StrategyParam<int> _highLevel;
	private readonly StrategyParam<int> _middleLevel;
	private readonly StrategyParam<int> _lowLevel;
	private readonly StrategyParam<bool> _buyOpen;
	private readonly StrategyParam<bool> _sellOpen;
	private readonly StrategyParam<bool> _buyClose;
	private readonly StrategyParam<bool> _sellClose;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private int? _prevSignal;

	/// <summary>
	/// Gamma parameter for the Laguerre filter.
	/// </summary>
	public decimal Gamma
	{
		get => _gamma.Value;
		set => _gamma.Value = value;
	}

	/// <summary>
	/// Upper threshold where overbought conditions are assumed.
	/// </summary>
	public int HighLevel
	{
		get => _highLevel.Value;
		set => _highLevel.Value = value;
	}

	/// <summary>
	/// Middle level separating bullish and bearish zones.
	/// </summary>
	public int MiddleLevel
	{
		get => _middleLevel.Value;
		set => _middleLevel.Value = value;
	}

	/// <summary>
	/// Lower threshold where oversold conditions are assumed.
	/// </summary>
	public int LowLevel
	{
		get => _lowLevel.Value;
		set => _lowLevel.Value = value;
	}

	/// <summary>
	/// Allow opening long positions.
	/// </summary>
	public bool BuyOpen
	{
		get => _buyOpen.Value;
		set => _buyOpen.Value = value;
	}

	/// <summary>
	/// Allow opening short positions.
	/// </summary>
	public bool SellOpen
	{
		get => _sellOpen.Value;
		set => _sellOpen.Value = value;
	}

	/// <summary>
	/// Allow closing long positions on opposite signals.
	/// </summary>
	public bool BuyClose
	{
		get => _buyClose.Value;
		set => _buyClose.Value = value;
	}

	/// <summary>
	/// Allow closing short positions on opposite signals.
	/// </summary>
	public bool SellClose
	{
		get => _sellClose.Value;
		set => _sellClose.Value = value;
	}

	/// <summary>
	/// Stop loss as percent of entry price.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of <see cref="ColorLaguerreStrategy"/>.
	/// </summary>
	public ColorLaguerreStrategy()
	{
		_gamma = Param(nameof(Gamma), 0.7m)
			.SetRange(0.1m, 0.9m)
			.SetDisplay("Gamma", "Laguerre filter gamma", "Indicators")
			;

		_highLevel = Param(nameof(HighLevel), 85)
			.SetRange(50, 95)
			.SetDisplay("High Level", "Upper oscillator level", "Indicators");

		_middleLevel = Param(nameof(MiddleLevel), 50)
			.SetRange(10, 90)
			.SetDisplay("Middle Level", "Middle oscillator level", "Indicators");

		_lowLevel = Param(nameof(LowLevel), 15)
			.SetRange(5, 40)
			.SetDisplay("Low Level", "Lower oscillator level", "Indicators");

		_buyOpen = Param(nameof(BuyOpen), true)
			.SetDisplay("Buy Open", "Allow opening long positions", "Trading");

		_sellOpen = Param(nameof(SellOpen), true)
			.SetDisplay("Sell Open", "Allow opening short positions", "Trading");

		_buyClose = Param(nameof(BuyClose), true)
			.SetDisplay("Buy Close", "Allow closing longs", "Trading");

		_sellClose = Param(nameof(SellClose), true)
			.SetDisplay("Sell Close", "Allow closing shorts", "Trading");

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetRange(0.5m, 5m)
			.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

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

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

		// Use RSI as a proxy for the Laguerre oscillator
		var rsi = new RelativeStrengthIndex { Length = 14 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(rsi, ProcessCandle)
			.Start();

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

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

		if (!rsiValue.IsFormed)
			return;

		var value = rsiValue.GetValue<decimal>();
		int signal = value >= MiddleLevel ? 2 : 1;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevSignal = signal;
			return;
		}

		if (_prevSignal is not int prev)
		{
			_prevSignal = signal;
			return;
		}

		if (prev == 1 && signal == 2 && Position <= 0)
		{
			BuyMarket();
		}
		else if (prev == 2 && signal == 1 && Position >= 0)
		{
			SellMarket();
		}

		_prevSignal = signal;

		if (Position > 0 && value <= LowLevel && SellClose)
			SellMarket();
		else if (Position < 0 && value >= HighLevel && BuyClose)
			BuyMarket();
	}
}