在 GitHub 上查看

Candle Trader策略

概述

Candle Trader策略 分析最近4根K线的开收价方向,寻找短期的逆转机会。该策略在单一品种上运行,使用市价委托,并为每笔交易设置预先确定的止盈和止损。

策略逻辑

  1. 做多直接入场 – 最近一根为阳线,前两根为阴线。
  2. 做多连续入场 – 最近一根阳线,前一根阴线,再前两根阳线,仅在启用 Continuation 时生效。
  3. 做空直接入场 – 最近一根为阴线,前两根阳线。
  4. 做空连续入场 – 最近一根阴线,前一根阳线,再前两根阴线,仅在启用 Continuation 时生效。
  5. 如果启用 Reverse Close,新信号与现有仓位相反,策略会先关闭相反仓位再入新仓。
  6. 所有订单均用预设的止盈、止损(价格步长)进行保护。

参数

名称 说明
Volume 单笔交易的量。
TakeProfitTicks 止盈距离(价格步长)。
StopLossTicks 止损距离(价格步长)。
Continuation 是否启用连续型入场。
ReverseClose 新信号出现时是否先关止相反仓位。
CandleType 分析所用的点价类型。

注意事项

  • 策略仅分析已完成的K线。
  • 每次发出市价委托前会取消所有未结委托。
  • 通过 StartProtection 设置止盈和止损。
  • Volume 参数可用于优化。
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 based on candle direction patterns.
/// Opens long or short positions depending on the directions of the last four candles.
/// </summary>
public class CandleTraderStrategy : Strategy
{
	private readonly StrategyParam<decimal> _volume;
	private readonly StrategyParam<decimal> _takeProfitTicks;
	private readonly StrategyParam<decimal> _stopLossTicks;
	private readonly StrategyParam<bool> _continuation;
	private readonly StrategyParam<bool> _reverseClose;
	private readonly StrategyParam<DataType> _candleType;

	private int _bar1Dir;
	private int _bar2Dir;
	private int _bar3Dir;
	private int _bar4Dir;

	/// <summary>
	/// Order volume.
	/// </summary>
	public decimal TradeVolume
	{
		get => _volume.Value;
		set => _volume.Value = value;
	}

	/// <summary>
	/// Take profit in price steps.
	/// </summary>
	public decimal TakeProfitTicks
	{
		get => _takeProfitTicks.Value;
		set => _takeProfitTicks.Value = value;
	}

	/// <summary>
	/// Stop loss in price steps.
	/// </summary>
	public decimal StopLossTicks
	{
		get => _stopLossTicks.Value;
		set => _stopLossTicks.Value = value;
	}

	/// <summary>
	/// Enable continuation pattern.
	/// </summary>
	public bool Continuation
	{
		get => _continuation.Value;
		set => _continuation.Value = value;
	}

	/// <summary>
	/// Close opposite position before opening a new one.
	/// </summary>
	public bool ReverseClose
	{
		get => _reverseClose.Value;
		set => _reverseClose.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public CandleTraderStrategy()
	{
		_volume = Param(nameof(TradeVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Volume", "Order volume", "General");

		_takeProfitTicks = Param(nameof(TakeProfitTicks), 500m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit Ticks", "Take profit in price steps", "Risk Management");

		_stopLossTicks = Param(nameof(StopLossTicks), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss Ticks", "Stop loss in price steps", "Risk Management");

		_continuation = Param(nameof(Continuation), true)
			.SetDisplay("Use Continuation", "Allow continuation pattern", "Trading Logic");

		_reverseClose = Param(nameof(ReverseClose), true)
			.SetDisplay("Reverse Close", "Close opposite position on signal", "Trading Logic");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_bar1Dir = _bar2Dir = _bar3Dir = _bar4Dir = 0;
	}

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

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

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

	private void ProcessCandle(ICandleMessage candle)
	{
		// Process only finished candles
		if (candle.State != CandleStates.Finished)
			return;

		// Shift stored directions
		_bar4Dir = _bar3Dir;
		_bar3Dir = _bar2Dir;
		_bar2Dir = _bar1Dir;

		// Determine direction of current candle
		_bar1Dir = candle.ClosePrice > candle.OpenPrice ? 1 : candle.ClosePrice < candle.OpenPrice ? -1 : 0;

		// Ensure sufficient history
		if (_bar4Dir == 0)
			return;

		var buyDirect = _bar1Dir == 1 && _bar2Dir == -1 && _bar3Dir == -1;
		var buyCont = _bar1Dir == 1 && _bar2Dir == -1 && _bar3Dir == 1 && _bar4Dir == 1 && Continuation;

		var sellDirect = _bar1Dir == -1 && _bar2Dir == 1 && _bar3Dir == 1;
		var sellCont = _bar1Dir == -1 && _bar2Dir == 1 && _bar3Dir == -1 && _bar4Dir == -1 && Continuation;

		if ((buyDirect || buyCont) && Position <= 0)
		{
			if (ReverseClose && Position < 0) BuyMarket();
			BuyMarket();
		}
		else if ((sellDirect || sellCont) && Position >= 0)
		{
			if (ReverseClose && Position > 0) SellMarket();
			SellMarket();
		}
	}
}