Ver en GitHub

Estrategia Hawaiian Tsunami Surfer

Esta estrategia busca picos repentinos de impulso y opera en su contra. Calcula el cambio porcentual del precio de cierre en una barra utilizando un indicador Momentum. Cuando el cambio porcentual supera un umbral mínimo, el movimiento se considera un "tsunami". La estrategia vende después de un fuerte pico alcista y compra después de un fuerte pico bajista. Se aplican stop-loss y take-profit de protección en pasos de precio a través de StartProtection.

Detalles

  • Criterios de entrada:
    • Vender cuando el porcentaje de momentum > TsunamiStrength.
    • Comprar cuando el porcentaje de momentum < -TsunamiStrength.
  • Largo/Corto: Ambas direcciones.
  • Criterios de salida: Stop-loss o take-profit de protección.
  • Stops: Sí, a través de StartProtection.
  • Valores predeterminados:
    • MomentumPeriod = 1
    • TsunamiStrength = 0.24
    • TakeProfitPoints = 500
    • StopLossPoints = 700
    • CandleType = TimeSpan.FromMinutes(1)
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: Momentum
    • Stops: Sí
    • Complejidad: Básico
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Alto
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;

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Contrarian strategy based on strong momentum spikes.
/// Sells when momentum percentage rises above a small threshold.
/// Buys when momentum drops below the negative threshold.
/// Uses protective stop-loss and take-profit in price steps.
/// </summary>
public class HawaiianTsunamiSurferStrategy : Strategy
{
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<decimal> _tsunamiStrength;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<DataType> _candleType;

	/// <summary>
	/// Momentum calculation period.
	/// </summary>
	public int MomentumPeriod
	{
		get => _momentumPeriod.Value;
		set => _momentumPeriod.Value = value;
	}

	/// <summary>
	/// Momentum deviation from 0% to trigger trades (in percent).
	/// </summary>
	public decimal TsunamiStrength
	{
		get => _tsunamiStrength.Value;
		set => _tsunamiStrength.Value = value;
	}

	/// <summary>
	/// Take-profit in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop-loss in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the <see cref="HawaiianTsunamiSurferStrategy"/>.
	/// </summary>
	public HawaiianTsunamiSurferStrategy()
	{
		_momentumPeriod = Param(nameof(MomentumPeriod), 1)
		.SetDisplay("Momentum Period", "Period of the momentum indicator", "Indicators")
		.SetGreaterThanZero();

		_tsunamiStrength = Param(nameof(TsunamiStrength), 0.24m)
		.SetDisplay("Threshold", "Momentum percentage deviation from 0%", "Parameters")
		.SetGreaterThanZero();

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500)
		.SetDisplay("Take Profit Points", "Take profit distance in price steps", "Risk Management")
		.SetGreaterThanZero();

		_stopLossPoints = Param(nameof(StopLossPoints), 700)
		.SetDisplay("Stop Loss Points", "Stop loss distance in price steps", "Risk Management")
		.SetGreaterThanZero();

		_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 OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		// Create momentum indicator
		var momentum = new Momentum { Length = MomentumPeriod };

		// Subscribe to candles and bind the indicator
		var subscription = SubscribeCandles(CandleType);
		subscription
		.Bind(momentum, ProcessCandle)
		.Start();

		// Setup chart
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, momentum);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal momentumValue)
	{
		// Process only finished candles
		if (candle.State != CandleStates.Finished)
		return;

		if (!IsFormedAndOnlineAndAllowTrading())
		return;

		// Convert momentum difference to percentage change around zero
		var closePrice = candle.ClosePrice;
		var previousPrice = closePrice - momentumValue;
		if (previousPrice == 0m)
		return;

		var percentChange = (momentumValue / previousPrice) * 100m;

		if (percentChange > TsunamiStrength && Position >= 0)
		{
			SellMarket();
		}
		else if (percentChange < -TsunamiStrength && Position <= 0)
		{
			BuyMarket();
		}
	}
}