Auf GitHub ansehen

Strategie Cidomo V1

Tages-Ausbruchsstrategie, die Trades platziert, wenn der Preis die jüngste Range verlässt.

Zusammenfassung

  • Typ: Ausbruch
  • Einstieg: Kauf, wenn der Preis über das höchste Hoch des Rückblickzeitraums ausbricht; Verkauf, wenn der Preis unter das niedrigste Tief fällt.
  • Ausstieg: Stop-Loss, Take-Profit, optionaler Breakeven und Trailing Stop.
  • Indikatoren: Highest, Lowest

Parameter

Name Beschreibung
Lookback Anzahl der Kerzen zur Berechnung der Range.
Delta Preisversatz, der zu den Ausbruchsniveaus addiert wird.
StopLoss Stop-Loss in Preispunkten.
TakeProfit Take-Profit in Preispunkten.
NoLoss Stop nach diesem Gewinn (Punkte) auf Einstiegspreis verschieben.
Trailing Trailing-Abstand in Punkten.
UseTimeFilter Wenn true, werden die Niveaus nach der angegebenen Zeit berechnet.
TradeTime Tageszeit zur Berechnung der Ausbruchsniveaus.
CandleType Kerzentyp für die Berechnungen.

Hinweise

Die Strategie überwacht nur abgeschlossene Kerzen. Niveaus werden einmal täglich nach TradeTime neu berechnet.

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