Ver en GitHub

Estrategia Angrybird xScalpingn

Angrybird xScalpingn es una estrategia de scalping de estilo martingala. Abre una operación inicial basada en la dirección del precio a corto plazo y un filtro RSI. Cuando el precio se mueve en contra de la posición abierta en un paso dinámico derivado del rango reciente, la estrategia añade otra operación con el volumen multiplicado por un factor. Todas las posiciones se cierran cuando el CCI muestra un fuerte movimiento contrario o cuando se alcanza el stop-loss o el take-profit.

Detalles

  • Criterios de entrada: La operación inicial sigue la dirección de cierre reciente con un filtro RSI. Las operaciones adicionales se abren cuando el precio se mueve en contra de la posición en el paso calculado.
  • Largo/Corto: Ambas direcciones.
  • Criterios de salida: Reversión del CCI o stop-loss/take-profit protector.
  • Stops: Sí.
  • Valores predeterminados:
    • Volume = 0.01
    • LotExponent = 2
    • DynamicPips = true
    • DefaultPips = 12
    • Depth = 24
    • Del = 3
    • TakeProfit = 20
    • StopLoss = 500
    • Drop = 500
    • RsiMinimum = 30
    • RsiMaximum = 70
    • MaxTrades = 10
    • CandleType = TimeSpan.FromMinutes(1)
  • Filtros:
    • Categoría: Grid
    • Dirección: Ambos
    • Indicadores: RSI, CCI
    • Stops: Sí
    • Complejidad: Avanzado
    • Marco temporal: Cualquiera
    • 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;



public class AngrybirdXScalpingnStrategy : Strategy
{
	private readonly StrategyParam<decimal> _lotExponent;
	private readonly StrategyParam<bool> _dynamicPips;
	private readonly StrategyParam<int> _defaultPips;
	private readonly StrategyParam<int> _depth;
	private readonly StrategyParam<int> _del;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _drop;
	private readonly StrategyParam<decimal> _rsiMinimum;
	private readonly StrategyParam<decimal> _rsiMaximum;
	private readonly StrategyParam<int> _maxTrades;
	private readonly StrategyParam<DataType> _candleType;

	private readonly Queue<decimal> _highs = new();
	private readonly Queue<decimal> _lows = new();
	private decimal? _lastBuyPrice;
	private decimal? _lastSellPrice;
	private int _tradeCount;
	private decimal _pipStep;
	private decimal _prevClose;

	public decimal LotExponent { get => _lotExponent.Value; set => _lotExponent.Value = value; }
	public bool DynamicPips { get => _dynamicPips.Value; set => _dynamicPips.Value = value; }
	public int DefaultPips { get => _defaultPips.Value; set => _defaultPips.Value = value; }
	public int Depth { get => _depth.Value; set => _depth.Value = value; }
	public int Del { get => _del.Value; set => _del.Value = value; }
	public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
	public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
	public decimal Drop { get => _drop.Value; set => _drop.Value = value; }
	public decimal RsiMinimum { get => _rsiMinimum.Value; set => _rsiMinimum.Value = value; }
	public decimal RsiMaximum { get => _rsiMaximum.Value; set => _rsiMaximum.Value = value; }
	public int MaxTrades { get => _maxTrades.Value; set => _maxTrades.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AngrybirdXScalpingnStrategy()
	{

		_lotExponent = Param(nameof(LotExponent), 2m)
		.SetDisplay("Lot Exponent", "Volume multiplier for additional trades", "General")
		.SetGreaterThanZero();

		_dynamicPips = Param(nameof(DynamicPips), false)
		.SetDisplay("Dynamic Pips", "Use dynamic grid step", "Parameters");

		_defaultPips = Param(nameof(DefaultPips), 12)
		.SetDisplay("Default Pips", "Base grid step in ticks", "Parameters")
		.SetGreaterThanZero();

		_depth = Param(nameof(Depth), 24)
		.SetDisplay("Depth", "Bars lookback for dynamic step", "Parameters")
		.SetGreaterThanZero();

		_del = Param(nameof(Del), 3)
		.SetDisplay("Del", "Divider for range calculation", "Parameters")
		.SetGreaterThanZero();

		_takeProfit = Param(nameof(TakeProfit), 20m)
		.SetDisplay("Take Profit", "Take profit in ticks", "Risk")
		.SetNotNegative();

		_stopLoss = Param(nameof(StopLoss), 500m)
		.SetDisplay("Stop Loss", "Stop loss in ticks", "Risk")
		.SetNotNegative();

		_drop = Param(nameof(Drop), 500m)
		.SetDisplay("CCI Drop", "CCI threshold for exit", "Parameters")
		.SetNotNegative();

		_rsiMinimum = Param(nameof(RsiMinimum), 30m)
		.SetDisplay("RSI Minimum", "Minimum RSI to allow short", "Parameters")
		.SetNotNegative();

		_rsiMaximum = Param(nameof(RsiMaximum), 70m)
		.SetDisplay("RSI Maximum", "Maximum RSI to allow long", "Parameters")
		.SetNotNegative();

		_maxTrades = Param(nameof(MaxTrades), 2)
		.SetDisplay("Max Trades", "Maximum number of open trades", "Risk")
		.SetGreaterThanZero();

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

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

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

		_highs.Clear();
		_lows.Clear();
		_lastBuyPrice = _lastSellPrice = null;
		_tradeCount = 0;
		_pipStep = 0m;
		_prevClose = 0m;
	}

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

		var rsi = new RelativeStrengthIndex { Length = 14 };
		var cci = new CommodityChannelIndex { Length = 55 };

		var sub = SubscribeCandles(CandleType);
		sub.Bind(rsi, cci, ProcessCandle)
		.Start();

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

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, sub);
			DrawIndicator(area, rsi);
			DrawIndicator(area, cci);
		}
	}

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

		var close = candle.ClosePrice;
		var high = candle.HighPrice;
		var low = candle.LowPrice;

		// calculate dynamic grid step
		if (DynamicPips)
		{
			_highs.Enqueue(high);
			_lows.Enqueue(low);

			if (_highs.Count > Depth)
			{
				_highs.Dequeue();
				_lows.Dequeue();
			}

			if (_highs.Count == Depth)
			{
				var highs = _highs.ToArray();
				var lows = _lows.ToArray();
				var highest = highs.Max();
				var lowest = lows.Min();
				var step = (highest - lowest) / Del;
				var stepSize = Security?.PriceStep ?? 1m;
				var minStep = (DefaultPips / (decimal)Del) * stepSize;
				var maxStep = (DefaultPips * Del) * stepSize;
				step = Math.Min(Math.Max(step, minStep), maxStep);
				_pipStep = step;
			}
		}
		else
		{
			_pipStep = DefaultPips * (Security?.PriceStep ?? 1m);
		}

		if (Position == 0)
		{
			_lastBuyPrice = _lastSellPrice = null;
			_tradeCount = 0;
		}

		// close all on CCI reversal
		if (Position > 0 && cci < -Drop)
		{
			if (Position > 0) SellMarket(Math.Abs(Position));
		else if (Position < 0) BuyMarket(Math.Abs(Position));
			return;
		}

		if (Position < 0 && cci > Drop)
		{
			if (Position > 0) SellMarket(Math.Abs(Position));
		else if (Position < 0) BuyMarket(Math.Abs(Position));
			return;
		}

		if (Position != 0)
		{
			if (_tradeCount < MaxTrades)
			{
				if (Position > 0 && _lastBuyPrice is decimal lb && lb - close >= _pipStep)
				{
					var vol = Volume * (decimal)Math.Pow((double)LotExponent, _tradeCount);
					BuyMarket(vol);
					_lastBuyPrice = close;
					_tradeCount++;
				}
				else if (Position < 0 && _lastSellPrice is decimal ls && close - ls >= _pipStep)
				{
					var vol = Volume * (decimal)Math.Pow((double)LotExponent, _tradeCount);
					SellMarket(vol);
					_lastSellPrice = close;
					_tradeCount++;
				}
			}

			_prevClose = close;
			return;
		}

		// first trade decision
		if (_prevClose != 0m)
		{
			if (_prevClose > close && rsi > RsiMinimum)
			{
				SellMarket(Volume);
				_lastSellPrice = close;
				_tradeCount = 1;
			}
			else if (_prevClose <= close && rsi < RsiMaximum)
			{
				BuyMarket(Volume);
				_lastBuyPrice = close;
				_tradeCount = 1;
			}
		}

		_prevClose = close;
	}
}