Ver en GitHub

Estrategia de FitFul 13

Descripción general

El asesor experto FitFul 13 trabaja alrededor de niveles de pivot semanales derivados de la semana de trading anterior. Espera a que la vela H1 actual (marco temporal predeterminado) reaccione a una de las bandas de pivot y confirma el movimiento con dos velas más antiguas de una serie de confirmación M15. Cuando la confirmación está presente, la estrategia abre una posición con niveles precalculados de stop-loss y take-profit derivados de la misma estructura de pivots. Un trailing stop protege las operaciones rentables una vez que el precio avanza lo suficiente.

Lógica original

  1. Calcular el precio típico y la estructura de pivot de la semana anterior: PriceTypical, R1, S1, niveles intermedios (R0.5, S0.5, R1.5, etc.) y las extensiones de segundo/tercer orden.
  2. Observar la vela H1 más reciente. Si cerró alcista, buscar en el cuerpo de la vela precedente un cruce hacia arriba de uno de los niveles de pivot. Si ocurre tal cruce, preparar parámetros largos: stop por debajo del soporte relevante, take-profit por encima de la resistencia emparejada. Para cierres bajistas, la lógica espejo prepara parámetros cortos.
  3. Si el cuerpo de la vela H1 no interactuó con ningún pivot, verificar dos velas M15 anteriores. Dos mínimos consecutivos perforando el mismo nivel confirman configuraciones largas, mientras que dos máximos cayendo a través de un nivel confirman cortos. Cada combinación se mapea a su propio par de stop/take.
  4. Enviar una orden de mercado con el volumen neto configurado. El port de StockSharp trabaja con posiciones netas, por lo tanto la exposición opuesta se aplana antes de abrir la nueva operación. Los precios de stop-loss y take-profit se almacenan internamente y se aplican mediante salidas virtuales en nuevas velas.
  5. Aplicar un trailing stop virtual: una vez que el beneficio abierto supere TrailingStopPips + TrailingStepPips, mover el stop a close - TrailingStopPips (largo) o close + TrailingStopPips (corto). El stop nunca retrocede y solo se ajusta si el precio avanza al menos el paso de trailing.
  6. Ignorar nuevas señales si la posición neta absoluta ya equivale a Volume × MaxPositions.

Parámetros

Nombre Tipo Predeterminado Descripción
CandleType DataType H1 Marco temporal principal usado para evaluar reacciones de pivot.
ConfirmationCandleType DataType M15 Marco temporal inferior que proporciona la confirmación de dos barras.
Volume decimal 0.1 Volumen de orden neto para cada entrada.
MaxPositions int 3 Exposición neta máxima expresada como múltiplos de Volume.
IndentPips decimal 3 Desplazamiento aplicado a los cálculos de stop-loss y take-profit basados en pivots.
TrailingStopPips decimal 150 Distancia del trailing stop en pips. Establecer en cero para deshabilitar el trailing.
TrailingStepPips decimal 5 Progreso de precio adicional mínimo (en pips) requerido antes de ajustar el trailing stop.

Notas sobre el port

  • StockSharp gestiona una única posición neta. La capacidad de cobertura original se emula aplanando la exposición opuesta cuando se toma una nueva entrada.
  • La lógica de stop-loss, take-profit y trailing se implementa virtualmente. La estrategia cierra posiciones en actualizaciones de velas cuando el precio cruza los niveles almacenados.
  • Los pivots semanales se recalculan cada vez que se recibe una nueva vela semanal. La confirmación predeterminada usa H1/M15, pero ambos marcos temporales pueden ajustarse mediante parámetros.
  • Todos los comentarios en el código fuente están escritos en inglés según lo requerido por las directrices de conversión.
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;

public class FitFul13Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public FitFul13Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 13).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}