Ver no GitHub

Estratégia Rock Trader Neuro

Estratégia que opera utilizando Bandas de Bollinger e um neurônio simples. As últimas sete larguras das Bandas de Bollinger são normalizadas para o intervalo [-1,1] e combinadas com pesos fixos. A soma ponderada é passada por uma ativação de tangente hiperbólica. Uma saída negativa abre uma posição comprada, enquanto uma saída positiva abre uma posição vendida. As posições são fechadas por stop loss ou take profit.

Detalhes

  • Critérios de entrada:
    • Comprado: saída do neurônio < 0
    • Vendido: saída do neurônio > 0
  • Comprado/Vendido: Ambos
  • Critérios de saída:
    • Stop loss ou take profit atingido
  • Stops: Distância absoluta de preço
  • Valores padrão:
    • StopLoss = 30
    • TakeProfit = 100
    • Lot = 1
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoria: Neural
    • Direção: Ambos
    • Indicadores: Bollinger Bands
    • Stops: Sim
    • Complexidade: Intermediário
    • Período: Curto prazo
    • Sazonalidade: Não
    • Redes neurais: Sim
    • Divergência: Não
    • Nível de risco: Médio
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>
/// Strategy using Bollinger Bands and a simple neuron to generate signals.
/// </summary>
public class RockTraderNeuroStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _lot;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _band1;
	private decimal _band2;
	private decimal _band3;
	private decimal _band4;
	private decimal _band5;
	private decimal _band6;
	private decimal _band7;

	private decimal _entryPrice;
	private decimal _stopPrice;
	private decimal _takePrice;

	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	public decimal Lot
	{
		get => _lot.Value;
		set => _lot.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public RockTraderNeuroStrategy()
	{
		_stopLoss = Param(nameof(StopLoss), 30m)
			.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 100m)
			.SetDisplay("Take Profit", "Take profit in price units", "Risk");

		_lot = Param(nameof(Lot), 1m)
			.SetDisplay("Lot", "Lots to trade", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();

		_band1 = _band2 = _band3 = _band4 = _band5 = _band6 = _band7 = 0m;
		_entryPrice = 0m;
		_stopPrice = 0m;
		_takePrice = 0m;
	}

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

		Volume = Lot;
		_band1 = _band2 = _band3 = _band4 = _band5 = _band6 = _band7 = 0m;
		_entryPrice = 0m;
		_stopPrice = 0m;
		_takePrice = 0m;

		var bb = new BollingerBands
		{
			Length = 20,
			Width = 2m
		};

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

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

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

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

		// Calculate current Bollinger Band width
		var bandWidth = middle == 0m ? 0m : (upper - lower) / middle;

		// Shift previous values to keep last seven widths
		_band7 = _band6;
		_band6 = _band5;
		_band5 = _band4;
		_band4 = _band3;
		_band3 = _band2;
		_band2 = _band1;
		_band1 = bandWidth;

		// Wait until all values are filled
		if (_band7 == 0m)
			return;

		var min = Math.Min(Math.Min(Math.Min(_band1, _band2), Math.Min(_band3, _band4)), Math.Min(_band5, Math.Min(_band6, _band7)));
		var max = Math.Max(Math.Max(Math.Max(_band1, _band2), Math.Max(_band3, _band4)), Math.Max(_band5, Math.Max(_band6, _band7)));

		if (max == min)
			return;

		const decimal d1 = -1m;
		const decimal d2 = 1m;

		decimal Normalize(decimal x) => (x - min) * (d2 - d1) / (max - min) + d1;

		var n1 = Normalize(_band1);
		var n2 = Normalize(_band2);
		var n3 = Normalize(_band3);
		var n4 = Normalize(_band4);
		var n5 = Normalize(_band5);
		var n6 = Normalize(_band6);
		var n7 = Normalize(_band7);

		// Weighted sum of inputs
		var net = n1 * 0.8m + n2 * -0.9m + n3 * 0.7m + n4 * 0.9m + n5 * -1m + n6 * 0.5m + n7 * 0m;

		// Activation using hyperbolic tangent
		var output = Tanh(net * 2m);

		if (Position > 0)
		{
			if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
				SellMarket();
		}
		else if (Position < 0)
		{
			if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
				BuyMarket();
		}
		else
		{
			if (output < -0.5m)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = _entryPrice - StopLoss;
				_takePrice = _entryPrice + TakeProfit;
			}
			else if (output > 0.5m)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = _entryPrice + StopLoss;
				_takePrice = _entryPrice - TakeProfit;
			}
		}
	}

	private static decimal Tanh(decimal x)
	{
		var d = Math.Clamp((double)x, -20d, 20d);
		var ePos = Math.Exp(d);
		var eNeg = Math.Exp(-d);
		return (decimal)((ePos - eNeg) / (ePos + eNeg));
	}
}