ニューラル ネットワーク テンプレート戦略
概要
この戦略は、RSI および MACD の機能をニューラル ネットワークにフィードする MQL5 エキスパート アドバイザー テンプレートの動作を複製します。 StockSharp には元のプロジェクトのカスタム ネットワーク ローダーが同梱されていないため、この戦略では、同じ市場構造とリスク管理を維持しながら、ブラックボックス ネットワークを決定論的なスコアリング モデルに置き換えます。目標は、RSI と MACD の両方が方向性に同意し、予想される動きが取引を正当化するのに十分な大きさであるときに勢いを捉えることです。
指標とデータ
- 相対強度指数 (RSI、12 期間) はローソク足の終値で計算され、元の典型的な価格入力を反映しています。
- Moving Average Convergence Divergence (MACD 12/48/12) は、運動量ヒストグラムおよび信頼度の代用として使用されます。
- 時間枠 構成可能。デフォルトはソースの専門家と一致する 5 分足のローソク足です。
取引ロジック
- 終了したローソクごとに、戦略は
BarsToPatternによって制御されるウィンドウを使用して、RSI と MACD のヒストグラム値のローリング キューを更新します。 - 50 からの RSI の偏差と、ローリング平均からの MACD ヒストグラムの偏差は、双曲線正接を使用して信頼スコアに結合され、ネットワークのスカッシング関数をエミュレートします。
- 絶対信頼度が
TradeLevelを超え、ポイントに変換された予測移動がMinTargetPointsを超えている場合、ストラテジーはスコアが示唆する方向に成行注文を発行します。 - 予想される動きに
ProfitMultiplyを乗算し、MaxTakeProfitPointsを上限とする動的なテイクプロフィットが、手動の手仕舞い処理のために保存されます。ポイント単位の対称ストップロスは、元の動作を反映します。 - ポジションがオープンしている間、ストラテジーは終了したローソク足をすべてチェックします。価格が保存されているストップまたはターゲットに達すると、市場でポジションをクローズし、内部状態をリセットします。
パラメーター
| パラメータ | 説明 |
|---|---|
BarsToPattern |
RSI および MACD 統計の計算に使用されるローリング ウィンドウに保存されているローソク足の数。 |
TradeLevel |
ポジションをオープンするために必要な最小の信頼度 (0 ~ 1)。 |
ProfitMultiply |
MaxTakeProfitPoints でキャップする前に、予想される動きに適用される乗数。 |
MinTargetPoints |
取引に参加するために予測から必要な価格ポイントの最小数。 |
MaxTakeProfitPoints |
テイクプロフィットに許可される最大距離 (ポイント単位)。 |
StopLossPoints |
エントリー価格に対する保護ストップまでの距離 (ポイント単位)。 |
TradeVolume |
すべての成行注文で送信されるボリューム。 |
CandleType |
サブスクライブするローソク足のデータ タイプまたはタイムフレーム。 |
注意事項
- 信頼モデルは、元のニューラル ネットワーク アプローチの構造を維持しながら動作の透明性を保つために、意図的に決定論的になっています。
- MQL5 バージョンがネットワーク出力を使用する方法と同様に、各取引が独自の動的ターゲットを維持できるように、テイクプロフィットとストップロスのレベルは手動で管理されます。
- この戦略は、オープンなポジションがない場合にのみ新しいエントリーを評価し、ソースエキスパートアドバイザーの単一ポジション制約を複製します。
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>
/// Momentum strategy inspired by a neural-network driven template from MQL5.
/// </summary>
public class NeuralNetworkTemplateStrategy : Strategy
{
private readonly StrategyParam<int> _barsToPattern;
private readonly StrategyParam<int> _maxTakeProfitPoints;
private readonly StrategyParam<int> _minTargetPoints;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<decimal> _profitMultiply;
private readonly StrategyParam<decimal> _tradeLevel;
private readonly StrategyParam<decimal> _volume;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi = null!;
private MovingAverageConvergenceDivergenceSignal _macd = null!;
private readonly Queue<decimal> _rsiHistory = new();
private readonly Queue<decimal> _macdHistory = new();
private decimal _rsiSum;
private decimal _macdSum;
private decimal? _targetPrice;
private decimal? _stopPrice;
private int _positionDirection;
/// <summary>
/// Number of candles used for pattern recognition.
/// </summary>
public int BarsToPattern
{
get => _barsToPattern.Value;
set => _barsToPattern.Value = value;
}
/// <summary>
/// Upper bound for calculated take-profit in points.
/// </summary>
public int MaxTakeProfitPoints
{
get => _maxTakeProfitPoints.Value;
set => _maxTakeProfitPoints.Value = value;
}
/// <summary>
/// Minimum projected move required to open a trade.
/// </summary>
public int MinTargetPoints
{
get => _minTargetPoints.Value;
set => _minTargetPoints.Value = value;
}
/// <summary>
/// Stop-loss distance in points.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Multiplier applied to the projected move returned by the scoring model.
/// </summary>
public decimal ProfitMultiply
{
get => _profitMultiply.Value;
set => _profitMultiply.Value = value;
}
/// <summary>
/// Required confidence level before opening a new position.
/// </summary>
public decimal TradeLevel
{
get => _tradeLevel.Value;
set => _tradeLevel.Value = value;
}
/// <summary>
/// Trading volume for every market order.
/// </summary>
public decimal TradeVolume
{
get => _volume.Value;
set => _volume.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="NeuralNetworkTemplateStrategy"/>.
/// </summary>
public NeuralNetworkTemplateStrategy()
{
_barsToPattern = Param(nameof(BarsToPattern), 3)
.SetGreaterThanZero()
.SetDisplay("Bars", "Candles analysed", "Model")
;
_maxTakeProfitPoints = Param(nameof(MaxTakeProfitPoints), 500)
.SetGreaterThanZero()
.SetDisplay("Max TP", "Maximum take-profit in points", "Risk");
_minTargetPoints = Param(nameof(MinTargetPoints), 1)
.SetGreaterThanZero()
.SetDisplay("Min Target", "Minimum projected move in points", "Model");
_stopLossPoints = Param(nameof(StopLossPoints), 300)
.SetGreaterThanZero()
.SetDisplay("Stop-Loss", "Stop-loss distance in points", "Risk");
_profitMultiply = Param(nameof(ProfitMultiply), 0.8m)
.SetNotNegative()
.SetDisplay("Profit Mult", "Take-profit multiplier", "Model");
_tradeLevel = Param(nameof(TradeLevel), 0.1m)
.SetRange(0m, 1m)
.SetDisplay("Trade Level", "Required confidence", "Model");
_volume = Param(nameof(TradeVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Order volume", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("TF", "Working timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null!;
_macd = null!;
_rsiHistory.Clear();
_macdHistory.Clear();
_rsiSum = 0m;
_macdSum = 0m;
_targetPrice = null;
_stopPrice = null;
_positionDirection = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = 12 };
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = 12 },
LongMa = { Length = 48 }
},
SignalMa = { Length = 12 }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_rsi, _macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawIndicator(area, _macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
ManageOpenPosition(candle);
if (!_rsi.IsFormed || !_macd.IsFormed)
return;
var rsiDecimal = rsiValue.ToDecimal();
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macdComponents)
return;
if (macdComponents.Macd is not decimal macdLine ||
macdComponents.Signal is not decimal signalLine)
return;
UpdateHistory(rsiDecimal, macdLine - signalLine);
if (Position != 0)
return;
EvaluateEntry(candle, rsiDecimal, macdLine, signalLine);
}
private void UpdateHistory(decimal rsiValue, decimal macdHistogram)
{
_rsiHistory.Enqueue(rsiValue);
_rsiSum += rsiValue;
if (_rsiHistory.Count > BarsToPattern)
_rsiSum -= _rsiHistory.Dequeue();
_macdHistory.Enqueue(macdHistogram);
_macdSum += macdHistogram;
if (_macdHistory.Count > BarsToPattern)
_macdSum -= _macdHistory.Dequeue();
}
private void EvaluateEntry(ICandleMessage candle, decimal rsiValue, decimal macdLine, decimal signalLine)
{
if (_rsiHistory.Count < BarsToPattern || _macdHistory.Count < BarsToPattern)
return;
var priceStep = Security?.PriceStep ?? 1m;
if (priceStep <= 0m)
priceStep = 1m;
var normalizedRsi = Clamp((rsiValue - 50m) / 50m, -1m, 1m);
var macdHistogram = macdLine - signalLine;
var macdAverage = _macdHistory.Count == 0 ? 0m : _macdSum / _macdHistory.Count;
var macdDeviation = macdHistogram - macdAverage;
var normalizedMomentum = (decimal)Math.Tanh((double)(macdDeviation * 5m));
var combinedScore = normalizedRsi * 0.6m + normalizedMomentum * 0.4m;
var confidence = Math.Min(1m, Math.Abs(combinedScore));
var projectedMove = macdDeviation * BarsToPattern;
var projectedPoints = projectedMove / priceStep;
if (combinedScore > 0m)
{
if (confidence < TradeLevel)
return;
if (projectedPoints < MinTargetPoints)
return;
var takeProfit = candle.ClosePrice + Math.Min(projectedMove * ProfitMultiply, MaxTakeProfitPoints * priceStep);
var stopLoss = candle.ClosePrice - StopLossPoints * priceStep;
if (takeProfit <= candle.ClosePrice)
return;
BuyMarket(TradeVolume);
_targetPrice = takeProfit;
_stopPrice = stopLoss;
_positionDirection = 1;
}
else if (combinedScore < 0m)
{
if (confidence < TradeLevel)
return;
if (projectedPoints > -MinTargetPoints)
return;
var takeProfit = candle.ClosePrice + Math.Max(projectedMove * ProfitMultiply, -MaxTakeProfitPoints * priceStep);
var stopLoss = candle.ClosePrice + StopLossPoints * priceStep;
if (takeProfit >= candle.ClosePrice)
return;
SellMarket(TradeVolume);
_targetPrice = takeProfit;
_stopPrice = stopLoss;
_positionDirection = -1;
}
}
private void ManageOpenPosition(ICandleMessage candle)
{
if (Position > 0m)
{
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Math.Abs(Position));
ResetTargets();
return;
}
if (_targetPrice.HasValue && candle.HighPrice >= _targetPrice.Value)
{
SellMarket(Math.Abs(Position));
ResetTargets();
}
}
else if (Position < 0m)
{
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetTargets();
return;
}
if (_targetPrice.HasValue && candle.LowPrice <= _targetPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetTargets();
}
}
else if (_positionDirection != 0)
{
ResetTargets();
}
}
private void ResetTargets()
{
_targetPrice = null;
_stopPrice = null;
_positionDirection = 0;
}
private static decimal Clamp(decimal value, decimal min, decimal max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
from collections import deque
from datatype_extensions import *
from indicator_extensions import *
import math
class neural_network_template_strategy(Strategy):
def __init__(self):
super(neural_network_template_strategy, self).__init__()
self._bars_to_pattern = self.Param("BarsToPattern", 3).SetGreaterThanZero().SetDisplay("Bars", "Candles analysed", "Model")
self._max_tp_points = self.Param("MaxTakeProfitPoints", 500).SetGreaterThanZero().SetDisplay("Max TP", "Maximum take-profit in points", "Risk")
self._min_target = self.Param("MinTargetPoints", 1).SetGreaterThanZero().SetDisplay("Min Target", "Minimum projected move in points", "Model")
self._sl_points = self.Param("StopLossPoints", 300).SetGreaterThanZero().SetDisplay("Stop-Loss", "Stop-loss distance in points", "Risk")
self._profit_mult = self.Param("ProfitMultiply", 0.8).SetNotNegative().SetDisplay("Profit Mult", "Take-profit multiplier", "Model")
self._trade_level = self.Param("TradeLevel", 0.1).SetDisplay("Trade Level", "Required confidence", "Model")
self._trade_volume = self.Param("TradeVolume", 0.1).SetGreaterThanZero().SetDisplay("Volume", "Order volume", "Trading")
self._candle_type = self.Param("CandleType", tf(5)).SetDisplay("TF", "Working timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(neural_network_template_strategy, self).OnReseted()
self._rsi_history = deque()
self._macd_history = deque()
self._rsi_sum = 0
self._macd_sum = 0
self._target_price = None
self._stop_price = None
self._pos_dir = 0
def OnStarted2(self, time):
super(neural_network_template_strategy, self).OnStarted2(time)
self._rsi_history = deque()
self._macd_history = deque()
self._rsi_sum = 0
self._macd_sum = 0
self._target_price = None
self._stop_price = None
self._pos_dir = 0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = 12
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = 12
self._macd.Macd.LongMa.Length = 48
self._macd.SignalMa.Length = 12
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(self._rsi, self._macd, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, self._rsi)
self.DrawOwnTrades(area)
def OnProcess(self, candle, rsi_value, macd_value):
if candle.State != CandleStates.Finished:
return
self._manage_position(candle)
if not self._rsi.IsFormed or not self._macd.IsFormed:
return
rsi_dec = float(rsi_value)
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
macd_line = float(macd_line)
signal_line = float(signal_line)
histogram = macd_line - signal_line
self._update_history(rsi_dec, histogram)
if self.Position != 0:
return
self._evaluate_entry(candle, rsi_dec, macd_line, signal_line)
def _update_history(self, rsi_val, macd_hist):
bars = self._bars_to_pattern.Value
self._rsi_history.append(rsi_val)
self._rsi_sum += rsi_val
if len(self._rsi_history) > bars:
self._rsi_sum -= self._rsi_history.popleft()
self._macd_history.append(macd_hist)
self._macd_sum += macd_hist
if len(self._macd_history) > bars:
self._macd_sum -= self._macd_history.popleft()
def _evaluate_entry(self, candle, rsi_val, macd_line, signal_line):
bars = self._bars_to_pattern.Value
if len(self._rsi_history) < bars or len(self._macd_history) < bars:
return
price_step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
price_step = float(self.Security.PriceStep)
normalized_rsi = max(-1, min(1, (rsi_val - 50) / 50))
macd_hist = macd_line - signal_line
macd_avg = self._macd_sum / len(self._macd_history) if len(self._macd_history) > 0 else 0
macd_dev = macd_hist - macd_avg
normalized_momentum = math.tanh(macd_dev * 5)
combined = normalized_rsi * 0.6 + normalized_momentum * 0.4
confidence = min(1, abs(combined))
projected_move = macd_dev * bars
projected_points = projected_move / price_step
vol = self._trade_volume.Value
if combined > 0:
if confidence < self._trade_level.Value:
return
if projected_points < self._min_target.Value:
return
tp = candle.ClosePrice + min(projected_move * self._profit_mult.Value, self._max_tp_points.Value * price_step)
sl = candle.ClosePrice - self._sl_points.Value * price_step
if tp <= candle.ClosePrice:
return
self.BuyMarket(vol)
self._target_price = tp
self._stop_price = sl
self._pos_dir = 1
elif combined < 0:
if confidence < self._trade_level.Value:
return
if projected_points > -self._min_target.Value:
return
tp = candle.ClosePrice + max(projected_move * self._profit_mult.Value, -self._max_tp_points.Value * price_step)
sl = candle.ClosePrice + self._sl_points.Value * price_step
if tp >= candle.ClosePrice:
return
self.SellMarket(vol)
self._target_price = tp
self._stop_price = sl
self._pos_dir = -1
def _manage_position(self, candle):
if self.Position > 0:
if self._stop_price is not None and candle.LowPrice <= self._stop_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_targets()
return
if self._target_price is not None and candle.HighPrice >= self._target_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_targets()
elif self.Position < 0:
if self._stop_price is not None and candle.HighPrice >= self._stop_price:
self.BuyMarket(Math.Abs(self.Position))
self._reset_targets()
return
if self._target_price is not None and candle.LowPrice <= self._target_price:
self.BuyMarket(Math.Abs(self.Position))
self._reset_targets()
elif self._pos_dir != 0:
self._reset_targets()
def _reset_targets(self):
self._target_price = None
self._stop_price = None
self._pos_dir = 0
def CreateClone(self):
return neural_network_template_strategy()