在 GitHub 上查看

基础追踪止损

基础追踪止损策略将商品通道指数 (CCI) 与相对强弱指数 (RSI) 结合,并使用固定点数的追踪止损。在两个指标同时显示超卖或超买时,策略开仓并立即设置以点数表示的追踪止损。随着价格朝有利方向移动,止损位跟随趋势收紧利润。

测试表明年化收益约为 32%。该策略在外汇市场表现最佳。

由于止损水平持续跟随价格,趋势延续时风险会自动收缩。退出仅在触发追踪止损时发生。系统一次只持有一个仓位,可做多也可做空。

详情

  • 入场条件
    • 多头CCI 在 -150 到 -100 之间且 RSI 在 0 到 30 之间。
    • 空头CCI 在 100 到 250 之间且 RSI 在 70 到 100 之间。
  • 方向:双向。
  • 出场条件:触发追踪止损。
  • 止损:仅追踪止损。
  • 默认值
    • StopLossPips = 20
    • CciPeriod = 14
    • RsiPeriod = 14
    • CandleType = TimeSpan.FromMinutes(1)
  • 过滤器
    • 分类:动量
    • 方向:双向
    • 指标:CCI,RSI
    • 止损:有
    • 复杂度:入门
    • 时间框架:日内
    • 季节性:无
    • 神经网络:无
    • 背离:无
    • 风险等级:中等
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy implementing a basic trailing stop with CCI and RSI signals.
/// </summary>
public class BasicTrailingStopStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossPct;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _stopPrice;

	public decimal StopLossPct
	{
		get => _stopLossPct.Value;
		set => _stopLossPct.Value = value;
	}

	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public BasicTrailingStopStrategy()
	{
		_stopLossPct = Param(nameof(StopLossPct), 1.5m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss %", "Trailing stop distance as percentage", "Risk Management");

		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "Commodity Channel Index period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Relative Strength Index period", "Indicators");

		_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();
		_stopPrice = 0m;
	}

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

		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(cci, rsi, (candle, cciVal, rsiVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!cciVal.IsFormed || !rsiVal.IsFormed)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				ProcessCandle(candle, cciVal.ToDecimal(), rsiVal.ToDecimal());
			})
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal cciValue, decimal rsiValue)
	{
		var stopOffset = candle.ClosePrice * StopLossPct / 100m;

		if (Position > 0)
		{
			var newStop = candle.ClosePrice - stopOffset;
			if (newStop > _stopPrice)
				_stopPrice = newStop;

			if (candle.LowPrice <= _stopPrice)
			{
				SellMarket();
				_stopPrice = 0m;
			}

			return;
		}

		if (Position < 0)
		{
			var newStop = candle.ClosePrice + stopOffset;
			if (_stopPrice == 0m || newStop < _stopPrice)
				_stopPrice = newStop;

			if (candle.HighPrice >= _stopPrice)
			{
				BuyMarket();
				_stopPrice = 0m;
			}

			return;
		}

		// No position - evaluate entry signals
		var longSignal = cciValue < -50m && rsiValue < 40m;
		var shortSignal = cciValue > 50m && rsiValue > 60m;

		if (longSignal)
		{
			BuyMarket();
			_stopPrice = candle.ClosePrice - stopOffset;
		}
		else if (shortSignal)
		{
			SellMarket();
			_stopPrice = candle.ClosePrice + stopOffset;
		}
	}
}