在 GitHub 上查看

蜡烛趋势策略

概述

该策略根据连续蜡烛的方向开仓。 当连续出现指定数量的阳线时开多单;当连续出现相同数量的阴线时开空单。 当出现相反信号时,可平掉现有头寸。

参数

  • Candle Type:用于分析的蜡烛时间框架。
  • Trend Candles:触发操作所需的连续同方向蜡烛数量。
  • Take Profit %:可选的止盈百分比。
  • Stop Loss %:可选的止损百分比。
  • Enable Long Entry:允许开多。
  • Enable Short Entry:允许开空。
  • Enable Long Exit:允许在相反信号时平多。
  • Enable Short Exit:允许在相反信号时平空。

逻辑

  1. 订阅所选时间框架的蜡烛数据。
  2. 统计连续上涨和下跌蜡烛的数量。
  3. 当上涨计数达到设定值时:
    • 若允许则平空。
    • 若允许则开多。
  4. 当下跌计数达到设定值时:
    • 若允许则平多。
    • 若允许则开空。
  5. 使用 StartProtection 设置可选的保护单。

注意

  • 仅在蜡烛收盘后处理信号。
  • 策略使用 BuyMarketSellMarket 进行交易。
  • 代码中的注释均为英文。
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>
/// Strategy that trades based on consecutive candle directions.
/// Enters long after several bullish candles in a row and short after several bearish candles.
/// </summary>
public class CandleTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _trendCandles;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _takeProfitPercent;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<bool> _enableLongEntry;
	private readonly StrategyParam<bool> _enableShortEntry;
	private readonly StrategyParam<bool> _enableLongExit;
	private readonly StrategyParam<bool> _enableShortExit;

	private int _upCount;
	private int _downCount;

	/// <summary>
	/// Number of consecutive candles required to trigger an action.
	/// </summary>
	public int TrendCandles
	{
	    get => _trendCandles.Value;
	    set => _trendCandles.Value = value;
	}

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

	/// <summary>
	/// Take profit as percentage from entry price.
	/// </summary>
	public decimal TakeProfitPercent
	{
	    get => _takeProfitPercent.Value;
	    set => _takeProfitPercent.Value = value;
	}

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

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

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

	/// <summary>
	/// Allow closing long positions.
	/// </summary>
	public bool EnableLongExit
	{
	    get => _enableLongExit.Value;
	    set => _enableLongExit.Value = value;
	}

	/// <summary>
	/// Allow closing short positions.
	/// </summary>
	public bool EnableShortExit
	{
	    get => _enableShortExit.Value;
	    set => _enableShortExit.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="CandleTrendStrategy"/>.
	/// </summary>
	public CandleTrendStrategy()
	{
	    _trendCandles = Param(nameof(TrendCandles), 3)
	        .SetGreaterThanZero()
	        .SetDisplay("Trend Candles", "Number of candles in one direction", "General")
	        ;

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

	    _takeProfitPercent = Param(nameof(TakeProfitPercent), 0m)
	        .SetDisplay("Take Profit %", "Take profit percentage", "Risk Management")
	        ;
	    _stopLossPercent = Param(nameof(StopLossPercent), 0m)
	        .SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
	        ;

	    _enableLongEntry = Param(nameof(EnableLongEntry), true)
	        .SetDisplay("Enable Long Entry", "Permission to enter long", "General");

	    _enableShortEntry = Param(nameof(EnableShortEntry), true)
	        .SetDisplay("Enable Short Entry", "Permission to enter short", "General");

	    _enableLongExit = Param(nameof(EnableLongExit), true)
	        .SetDisplay("Enable Long Exit", "Permission to exit long", "General");

	    _enableShortExit = Param(nameof(EnableShortExit), true)
	        .SetDisplay("Enable Short Exit", "Permission to exit short", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
	    base.OnReseted();
	    _upCount = 0;
	    _downCount = 0;
	}

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

	    var tp = TakeProfitPercent > 0 ? new Unit(TakeProfitPercent, UnitTypes.Percent) : null;
	    var sl = StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : null;
	    StartProtection(tp, sl);

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

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

	    if (!IsFormedAndOnlineAndAllowTrading())
	        return;

	    var isBull = candle.ClosePrice > candle.OpenPrice;
	    var isBear = candle.ClosePrice < candle.OpenPrice;

	    if (isBull)
	    {
	        _upCount++;
	        _downCount = 0;
	    }
	    else if (isBear)
	    {
	        _downCount++;
	        _upCount = 0;
	    }
	    else
	    {
	        _upCount = 0;
	        _downCount = 0;
	    }

	    if (_upCount >= TrendCandles)
	    {
	        if (Position < 0 && EnableLongExit)
	            BuyMarket();

	        if (Position <= 0 && EnableLongEntry)
	            BuyMarket();
	    }
	    else if (_downCount >= TrendCandles)
	    {
	        if (Position > 0 && EnableShortExit)
	            SellMarket();

	        if (Position >= 0 && EnableShortEntry)
	            SellMarket();
	    }
	}
}