在 GitHub 上查看

TRIX 交叉策略

该策略使用两个不同周期的 TRIX(三重指数移动平均振荡器)指标来寻找潜在的反转点。当快速 TRIX 形成局部底部且慢速 TRIX 上升时开多;当快速 TRIX 形成局部顶部且慢速 TRIX 下降时开空。

参数

  • Fast TRIX Period – 快速 TRIX 指标周期。
  • Slow TRIX Period – 慢速 TRIX 指标周期。
  • Take Profit – 以绝对价格单位表示的止盈值。
  • Stop Loss – 以绝对价格单位表示的止损值。
  • Candle Type – 使用的 K 线类型或时间框。

交易逻辑

  1. 订阅选定的 K 线类型。
  2. 在每根完成的 K 线上计算快慢 TRIX 的数值。
  3. 当快速 TRIX 当前值高于前一值、前一值低于再前一值且慢速 TRIX 上升时开多。
  4. 当上述条件反向满足时开空。
  5. 同时仅持有一个仓位。
  6. 自动应用止损和止盈保护。

说明

该策略由 MQL5 脚本改编,展示了如何在 StockSharp 中使用 TRIX 指标。

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 based on fast and slow TRIX indicator signals.
/// A long position opens when the fast TRIX forms a local bottom and the slow TRIX is rising.
/// A short position opens when the fast TRIX forms a local top and the slow TRIX is falling.
/// </summary>
public class TrixCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<decimal> _minTrix;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<DataType> _candleType;

	// Store previous TRIX values for decision making
	private decimal _fastTrixPrev1;
	private decimal _fastTrixPrev2;
	private decimal _slowTrixPrev;
	private decimal _prevFastTema;
	private decimal _prevSlowTema;
	private TripleExponentialMovingAverage _fastTema = null!;
	private TripleExponentialMovingAverage _slowTema = null!;

	/// <summary>
	/// Fast TRIX period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow TRIX period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Minimum TRIX value required for a signal.
	/// </summary>
	public decimal MinTrix
	{
		get => _minTrix.Value;
		set => _minTrix.Value = value;
	}

	/// <summary>
	/// Take profit size in absolute price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Stop loss size in absolute price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

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

	/// <summary>
	/// Initializes <see cref="TrixCrossoverStrategy"/>.
	/// </summary>
	public TrixCrossoverStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Fast TRIX Period", "Period for the fast TRIX indicator", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow TRIX Period", "Period for the slow TRIX indicator", "Indicators");
		_minTrix = Param(nameof(MinTrix), 0.0005m)
			.SetGreaterThanZero()
			.SetDisplay("Min TRIX", "Minimum TRIX magnitude for signals", "Indicators");
		_takeProfit = Param(nameof(TakeProfit), 1500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take profit in absolute price units", "Risk Management");
		_stopLoss = Param(nameof(StopLoss), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop loss in absolute price units", "Risk Management");
		_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();

		_fastTrixPrev1 = 0m;
		_fastTrixPrev2 = 0m;
		_slowTrixPrev = 0m;
		_prevFastTema = 0m;
		_prevSlowTema = 0m;
		_fastTema = null!;
		_slowTema = null!;
	}

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

		_fastTrixPrev1 = 0m;
		_fastTrixPrev2 = 0m;
		_slowTrixPrev = 0m;
		_prevFastTema = 0m;
		_prevSlowTema = 0m;
		_fastTema = new TripleExponentialMovingAverage { Length = FastPeriod };
		_slowTema = new TripleExponentialMovingAverage { Length = SlowPeriod };

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

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

		StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
	}

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

		if (_prevFastTema == 0m || _prevSlowTema == 0m)
		{
			_prevFastTema = fastTemaValue;
			_prevSlowTema = slowTemaValue;
			return;
		}

		var fastTrix = (fastTemaValue - _prevFastTema) / _prevFastTema;
		var slowTrix = (slowTemaValue - _prevSlowTema) / _prevSlowTema;

		_prevFastTema = fastTemaValue;
		_prevSlowTema = slowTemaValue;

		var prevFastTrix = _fastTrixPrev1;
		_fastTrixPrev2 = _fastTrixPrev1;
		_fastTrixPrev1 = fastTrix;

		var slowTrixPrev = _slowTrixPrev;
		_slowTrixPrev = slowTrix;

		if (_fastTrixPrev2 == 0m || slowTrixPrev == 0m)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var crossUp = prevFastTrix <= 0 && fastTrix > 0;
		var crossDown = prevFastTrix >= 0 && fastTrix < 0;

		if (crossUp && slowTrix > MinTrix && Position <= 0)
			BuyMarket();
		else if (crossDown && slowTrix < -MinTrix && Position >= 0)
			SellMarket();
	}
}