Открыть на GitHub

Стратегия Trend Arrows

Стратегия торгует пробои, когда цена закрытия выходит за пределы последних экстремумов. Она рассчитывает максимальные и минимальные цены закрытия за заданный период. Новый восходящий тренд фиксируется при пробое верхнего экстремума, а нисходящий — при пробое нижнего.

При появлении сигнала вверх возможны закрытие коротких позиций и открытие длинных. Сигнал вниз позволяет закрыть длинные позиции и при необходимости открыть короткие. Стратегия обрабатывает только завершённые свечи и использует высокоуровневый API StockSharp.

Параметры

  • Period – количество баров для расчёта экстремумов.
  • Candle Type – таймфрейм свечей.
  • Open Long – разрешить открытие длинных позиций.
  • Open Short – разрешить открытие коротких позиций.
  • Close Long – разрешить закрытие длинных позиций.
  • Close Short – разрешить закрытие коротких позиций.

Логика

  1. Подписка на данные свечей выбранного таймфрейма.
  2. Отслеживание максимумов и минимумов закрытий с помощью индикаторов Highest и Lowest.
  3. Пробой выше максимума обозначает восходящий тренд, ниже минимума — нисходящий.
  4. Вход или выход из позиций согласно новому тренду и активным опциям.
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>
/// Trend arrows strategy based on breakouts of recent extremes.
/// Detects when price moves above the highest close or below the lowest close
/// over the specified period and trades in the direction of the breakout.
/// </summary>
public class TrendArrowsStrategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<DataType> _candleType;

	private bool _prevTrendUp;
	private bool _prevTrendDown;
	private decimal? _prevHighest;
	private decimal? _prevLowest;

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

	public TrendArrowsStrategy()
	{
		_period = Param(nameof(Period), 15)
			.SetDisplay("Period", "Number of bars for extreme calculation", "Parameters")
			.SetOptimize(5, 30, 5);

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevTrendUp = false;
		_prevTrendDown = false;
		_prevHighest = null;
		_prevLowest = null;
	}

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

		_prevTrendUp = false;
		_prevTrendDown = false;
		_prevHighest = null;
		_prevLowest = null;

		var highest = new Highest { Length = Period };
		var lowest = new Lowest { Length = Period };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Use previous bar's highest/lowest to detect breakout
		if (_prevHighest is null || _prevLowest is null)
		{
			_prevHighest = highestVal;
			_prevLowest = lowestVal;
			return;
		}

		var trendUp = false;
		var trendDown = false;

		if (candle.ClosePrice > _prevHighest.Value)
			trendUp = true;
		else if (candle.ClosePrice < _prevLowest.Value)
			trendDown = true;
		else
		{
			trendUp = _prevTrendUp;
			trendDown = _prevTrendDown;
		}

		// Buy when up trend appears
		if (!_prevTrendUp && trendUp && Position <= 0)
			BuyMarket();

		// Sell when down trend appears
		if (!_prevTrendDown && trendDown && Position >= 0)
			SellMarket();

		_prevTrendUp = trendUp;
		_prevTrendDown = trendDown;
		_prevHighest = highestVal;
		_prevLowest = lowestVal;
	}
}