Ver en GitHub

Estrategia Exp Fishing

Esta estrategia entra en una posición cuando el cierre de la vela completada difiere de su apertura en al menos Price Step. Si la diferencia es positiva, compra; si es negativa, vende.

Después de abrir una posición, cada movimiento adicional de Price Step a favor de la operación dispara una orden de mercado adicional en la misma dirección hasta Max Orders. Stop-loss y take-profit de protección se aplican a cada posición usando distancias absolutas de precio.

Parámetros

  • Price Step – movimiento mínimo de precio (en unidades absolutas) requerido para abrir o agregar a una posición.
  • Max Orders – número máximo de órdenes de mercado permitidas en una dirección.
  • Stop Loss – distancia desde el precio de entrada donde se coloca un stop de protección.
  • Take Profit – distancia desde el precio de entrada donde se coloca el objetivo de ganancia.
  • Candle Type – marco temporal de velas usado para cálculos (por defecto 1 minuto).

Lógica de Trading

  1. Esperar una vela terminada.
  2. Si no hay posición abierta:
    • Comprar si Close - Open >= Price Step.
    • Vender si Open - Close >= Price Step.
  3. Cuando existe una posición:
    • Si el precio avanza Price Step desde la última entrada, agregar otra orden en la misma dirección.
    • Dejar de agregar órdenes una vez que el número alcanza Max Orders.
  4. Stop-loss y take-profit se gestionan automáticamente para cada orden.

La estrategia está adaptada del experto MQL5 "Exp Fishing" y demuestra un enfoque simple de seguimiento de tendencia estilo rejilla.

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>
/// Trend-following strategy that adds to position every time price moves by configured step.
/// Based on MQL5 Exp_Fishing expert.
/// </summary>
public class ExpFishingStrategy : Strategy
{
	private readonly StrategyParam<decimal> _priceStep;
	private readonly StrategyParam<int> _maxOrders;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _entryPrice;
	private int _ordersCount;
	private bool _isLong;

	/// <summary>
	/// Initializes a new instance of <see cref="ExpFishingStrategy"/>.
	/// </summary>
	public ExpFishingStrategy()
	{
		_priceStep = Param(nameof(PriceStep), 300m)
			.SetGreaterThanZero()
			.SetDisplay("Price Step", "Minimum price move to enter or add", "Parameters")
			;

		_maxOrders = Param(nameof(MaxOrders), 10)
			.SetGreaterThanZero()
			.SetDisplay("Max Orders", "Maximum number of orders in one direction", "Parameters")
			;

		_stopLoss = Param(nameof(StopLoss), 1000m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss", "Stop loss distance in price units", "Parameters")
			;

		_takeProfit = Param(nameof(TakeProfit), 2000m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Take profit distance in price units", "Parameters")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles for analysis", "Parameters");
	}

	/// <summary>
	/// Price move required to open or add position.
	/// </summary>
	public decimal PriceStep
	{
		get => _priceStep.Value;
		set => _priceStep.Value = value;
	}

	/// <summary>
	/// Maximum number of orders in single direction.
	/// </summary>
	public int MaxOrders
	{
		get => _maxOrders.Value;
		set => _maxOrders.Value = value;
	}

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

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

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_entryPrice = 0m;
		_ordersCount = 0;
		_isLong = false;
	}

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

		StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));

		SubscribeCandles(CandleType)
			.Bind(ProcessCandle)
			.Start();
	}

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

		var move = candle.ClosePrice - candle.OpenPrice;

		if (Position == 0)
		{
			_ordersCount = 0;

			if (move >= PriceStep)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_ordersCount = 1;
				_isLong = true;
			}
			else if (move <= -PriceStep)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_ordersCount = 1;
				_isLong = false;
			}

			return;
		}

		if (_ordersCount >= MaxOrders)
			return;

		if (_isLong)
		{
			if (candle.ClosePrice - _entryPrice >= PriceStep)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_ordersCount++;
			}
		}
		else
		{
			if (_entryPrice - candle.ClosePrice >= PriceStep)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_ordersCount++;
			}
		}
	}
}