Ver no GitHub

Estratégia Cidomo V1

Estratégia de rompimento diário que coloca negociações quando o preço escapa do intervalo recente.

Resumo

  • Tipo: Rompimento
  • Entrada: Compra quando o preço rompe acima da máxima mais alta do período de retrospectiva, vende quando o preço rompe abaixo da mínima mais baixa.
  • Saída: Stop loss, take profit, breakeven e trailing stop opcionais.
  • Indicadores: Highest, Lowest

Parâmetros

Nome Descrição
Lookback Número de velas utilizadas para calcular o intervalo.
Delta Deslocamento de preço adicionado aos níveis de rompimento.
StopLoss Stop loss em pontos de preço.
TakeProfit Take profit em pontos de preço.
NoLoss Mover o stop para o nível de entrada após este lucro (pontos).
Trailing Distância de trailing em pontos.
UseTimeFilter Se verdadeiro, os níveis são calculados após o horário especificado.
TradeTime Hora do dia para calcular os níveis de rompimento.
CandleType Tipo de vela utilizado para os cálculos.

Notas

A estratégia monitora apenas velas concluídas. Os níveis são recalculados uma vez por dia após TradeTime.

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>
/// Daily breakout strategy based on previous range.
/// </summary>
public class CidomoV1Strategy : Strategy
{
	private readonly StrategyParam<int> _lookback;
	private readonly StrategyParam<decimal> _delta;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _noLoss;
	private readonly StrategyParam<decimal> _trailing;
	private readonly StrategyParam<bool> _useTimeFilter;
	private readonly StrategyParam<TimeSpan> _tradeTime;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _longLevel;
	private decimal _shortLevel;
	private DateTime _lastTradeDay;
	private decimal _entryPrice;
	private decimal _stopPrice;

	private Highest _highest = null!;
	private Lowest _lowest = null!;

	/// <summary>
	/// Number of candles used to calculate range.
	/// </summary>
	public int Lookback
	{
		get => _lookback.Value;
		set => _lookback.Value = value;
	}

	/// <summary>
	/// Price offset added to breakout levels.
	/// </summary>
	public decimal Delta
	{
		get => _delta.Value;
		set => _delta.Value = value;
	}

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

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

	/// <summary>
	/// Move stop to entry after this profit (points).
	/// </summary>
	public decimal NoLoss
	{
		get => _noLoss.Value;
		set => _noLoss.Value = value;
	}

	/// <summary>
	/// Trailing distance in points.
	/// </summary>
	public decimal Trailing
	{
		get => _trailing.Value;
		set => _trailing.Value = value;
	}

	/// <summary>
	/// Trade only after specified time.
	/// </summary>
	public bool UseTimeFilter
	{
		get => _useTimeFilter.Value;
		set => _useTimeFilter.Value = value;
	}

	/// <summary>
	/// Time to calculate breakout levels.
	/// </summary>
	public TimeSpan TradeTime
	{
		get => _tradeTime.Value;
		set => _tradeTime.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public CidomoV1Strategy()
	{
		_lookback = Param(nameof(Lookback), 32)
			.SetGreaterThanZero()
			.SetDisplay("Lookback", "Number of candles to look back", "General")
			
			.SetOptimize(10, 60, 10);

		_delta = Param(nameof(Delta), 0m)
			.SetDisplay("Delta", "Price offset added to breakout levels", "General");

		_stopLoss = Param(nameof(StopLoss), 60m)
			.SetDisplay("Stop Loss", "Stop loss in points", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 70m)
			.SetDisplay("Take Profit", "Take profit in points", "Risk");

		_noLoss = Param(nameof(NoLoss), 35m)
			.SetDisplay("Break-even", "Move stop to entry after profit", "Risk");

		_trailing = Param(nameof(Trailing), 5m)
			.SetDisplay("Trailing", "Trailing distance in points", "Risk");

		_useTimeFilter = Param(nameof(UseTimeFilter), true)
			.SetDisplay("Use Time Filter", "Trade only after specified time", "General");

		_tradeTime = Param(nameof(TradeTime), new TimeSpan(9, 0, 0))
			.SetDisplay("Trade Time", "Time to calculate breakout", "General");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_longLevel = 0;
		_shortLevel = 0;
		_lastTradeDay = default;
		_entryPrice = 0;
		_stopPrice = 0;
		_highest = default;
		_lowest = default;
	}

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

		_highest = new Highest { Length = Lookback };
		_lowest = new Lowest { Length = Lookback };

		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);
		}

		StartProtection(null, null);
	}

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

		var step = Security.PriceStep ?? 1m;
		var time = candle.OpenTime.TimeOfDay;

		if ((!UseTimeFilter || time >= TradeTime) && _lastTradeDay != candle.OpenTime.Date)
		{
			_longLevel = highest + Delta * step;
			_shortLevel = lowest - Delta * step;
			_lastTradeDay = candle.OpenTime.Date;
			// Levels updated
		}


		if (Position == 0)
		{
			if (candle.HighPrice >= _longLevel)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = _entryPrice - StopLoss * step;
			}
			else if (candle.LowPrice <= _shortLevel)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = _entryPrice + StopLoss * step;
			}
		}
		else if (Position > 0)
		{
			if (candle.LowPrice <= _stopPrice)
			{
				SellMarket();
				return;
			}

			if (TakeProfit > 0 && candle.HighPrice >= _entryPrice + TakeProfit * step)
			{
				SellMarket();
				return;
			}

			if (NoLoss > 0 && _stopPrice < _entryPrice && candle.HighPrice >= _entryPrice + NoLoss * step)
				_stopPrice = _entryPrice;

			if (Trailing > 0 && candle.HighPrice >= _entryPrice + Trailing * step)
			{
				var newStop = candle.ClosePrice - Trailing * step;
				if (newStop > _stopPrice)
					_stopPrice = newStop;
			}
		}
		else if (Position < 0)
		{
			if (candle.HighPrice >= _stopPrice)
			{
				BuyMarket();
				return;
			}

			if (TakeProfit > 0 && candle.LowPrice <= _entryPrice - TakeProfit * step)
			{
				BuyMarket();
				return;
			}

			if (NoLoss > 0 && _stopPrice > _entryPrice && candle.LowPrice <= _entryPrice - NoLoss * step)
				_stopPrice = _entryPrice;

			if (Trailing > 0 && candle.LowPrice <= _entryPrice - Trailing * step)
			{
				var newStop = candle.ClosePrice + Trailing * step;
				if (newStop < _stopPrice)
					_stopPrice = newStop;
			}
		}
	}
}