GitHub で見る

Rock Trader Neuro戦略

ボリンジャーバンドと単純なニューロンを使用して取引する戦略です。 直近7本のボリンジャーバンド幅を[-1,1]の範囲に正規化し、 固定の重みと組み合わせます。加重和は双曲線正接活性化関数を通過します。 負の出力はロングポジションを建て、正の出力はショートポジションを建てます。 ポジションはストップロスまたはテイクプロフィットで決済されます。

詳細

  • エントリー条件:
    • ロング: ニューロン出力 < 0
    • ショート: ニューロン出力 > 0
  • ロング/ショート: 両方
  • エグジット条件:
    • ストップロスまたはテイクプロフィット到達
  • ストップ: 絶対価格距離
  • デフォルト値:
    • StopLoss = 30
    • TakeProfit = 100
    • Lot = 1
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • フィルター:
    • カテゴリ: Neural
    • 方向: 両方
    • インジケーター: Bollinger Bands
    • ストップ: はい
    • 複雑さ: 中級
    • 時間軸: 短期
    • 季節性: いいえ
    • ニューラルネットワーク: はい
    • ダイバージェンス: いいえ
    • リスクレベル: 中
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));
	}
}