Ver en GitHub

Estrategia de Spread Negativo

La estrategia Negative Spread aprovecha los momentos excepcionales en que el mejor precio de venta cae por debajo del mejor precio de compra, creando un spread negativo. Cuando aparece este desequilibrio de precios, la estrategia vende al mercado e intenta capturar el spread anormal. Después de abrir la posición corta, se cierra en la siguiente actualización del libro de órdenes cuando el mercado vuelve a un estado normal.

El sistema escucha únicamente eventos del libro de órdenes y no depende de velas ni indicadores. Se proporcionan parámetros opcionales de stop-loss y take-profit como medidas de seguridad, calculados en pips usando el tamaño del tick del instrumento.

Detalles

  • Criterios de entrada: BestAsk < BestBid y sin posición activa.
  • Largo/Corto: Solo corto.
  • Criterios de salida: La posición se cierra inmediatamente después de abrirse.
  • Stops: Stop-loss y take-profit opcionales en pips.
  • Valores predeterminados:
    • Volume = 1
    • TakeProfitPips = 5000
    • StopLossPips = 5000
  • Filtros:
    • Categoría: Arbitraje
    • Dirección: Corto
    • Indicadores: Ninguno
    • Stops: Opcional
    • Complejidad: Básico
    • Marco temporal: Tick
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Alto
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;

/// <summary>
/// Strategy that detects price dislocations using Bollinger Bands
/// and trades mean reversion when price extends beyond bands.
/// </summary>
public class NegativeSpreadStrategy : Strategy
{
	private readonly StrategyParam<int> _bbPeriod;
	private readonly StrategyParam<decimal> _bbWidth;
	private readonly StrategyParam<DataType> _candleType;

	public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
	public decimal BbWidth { get => _bbWidth.Value; set => _bbWidth.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public NegativeSpreadStrategy()
	{
		_bbPeriod = Param(nameof(BbPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");

		_bbWidth = Param(nameof(BbWidth), 1.5m)
			.SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
	}

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

		var bb = new BollingerBands { Length = BbPeriod, Width = BbWidth };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(bb, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(1, UnitTypes.Percent),
			stopLoss: new Unit(0.5m, UnitTypes.Percent)
		);

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, bb);
			DrawOwnTrades(area);
		}
	}

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

		if (!bbValue.IsFormed)
			return;

		var bb = (BollingerBandsValue)bbValue;
		if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
			return;

		var close = candle.ClosePrice;

		// Mean reversion: sell when above upper band, buy when below lower band
		if (close > upper && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}
		else if (close < lower && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
	}
}