GitHub で見る
統計ユークリッド指標戦略
概要
この戦略は、MetaTrader エキスパート アドバイザー Stat_Euclidean_Metric.mq4 の動作を再現します。単一の商品と時間枠での MACD の反転を監視します。 MACD ラインがローカルな転換点を形成すると、戦略は直ちにポジションをオープンするか (トレーニング モード)、現在の市場構造をバイナリ ファイルに保存されている過去の特徴ベクトルと比較する k 最近傍 (k-NN) 分類器を使用して設定を検証します。
取引ロジック
- 構成されたローソク足タイプをサブスクライブし、典型的な価格の MACD インジケーターを計算します ((高値 + 安値 + 終値) / 3 )。
- 最後の 3 つの完了した MACD 値が
MACD[2] <= MACD[1] と MACD[1] > MACD[0] を満たしたときに、弱気反転を検出します。
MACD[2] >= MACD[1] と MACD[1] < MACD[0] のときに強気の反転を検出します。
- 選択したモードに応じて次のようになります。
- トレーニング モード (
TrainingMode = true) – オプションで現在のポジションを閉じた後、反転の方向に成行注文を開きます。これは、新しいサンプルを収集するときの元の EA の動作を模倣します。
- 分類子モード (
TrainingMode = false) – 典型的な価格の単純移動平均の 5 つの比率を計算し、k-NN モデルで成功の確率を評価します。確率が設定されたしきい値を超えた場合にのみ注文を出します。
- 組み込みの
StartProtection モジュールを適用して、金融商品のステップでストップロスとテイクプロフィットのレベルを設定します。
分類のための特徴ベクトル
k-NN モデルは、閉じたばかりのローソク足で計算された次の比率を使用します。
- SMA(89) / SMA(144)
- SMA(144) / SMA(233)
- SMA(21) / SMA(89)
- SMA(55) / SMA(89)
- SMA(2) / SMA(55)
データセット ファイルに保存されている各サンプルには、6 つの double 値が含まれています。つまり、上記の 5 つの比率とラベル (不利な結果の場合は 0、取引が成功した場合は 1) です。評価中に、この戦略は最も近い NeighborCount サンプルを選択し、それらのラベルを平均して、結果を成功の確率として解釈します。
データセットファイル
BuyDatasetPath – 強気取引後に収集されたベクトルを含むバイナリ ファイルへのパス。
SellDatasetPath – 弱気取引後に収集されたベクトルを含むバイナリ ファイルへのパス。
パスが相対パスの場合、Environment.CurrentDirectory に対して解決されます。欠落したファイルはログに報告され、空のデータセットとして扱われます。この実装はデータセットを読み取りますが、新しいサンプルを自動的に更新または追加しません。新しいベクトルのエクスポートは、トレーニング モードで実行するときに外部で処理する必要があります。
パラメーター
- トレーニングモード – 純粋な MACD 取引と分類子支援取引を切り替えます。
- BuyThreshold / SellThreshold – 主方向で取引を開始するために分類子によって返される最小確率。
- AllowInverseEntries – 確率が非常に低い場合に、逆張り取引を有効にします。
- InverseBuyThreshold / InverseSellThreshold – 逆方向取引をトリガーする最大確率。
- FastLength / SlowLength / SignalLength – MACD EMA の長さ。
- TakeProfitPoints / StopLossPoints – 機器のステップで表される保護レベル。
- ClosePositionsOnSignal – 新しい注文を送信する前に現在のネット ポジションをクローズします。
- BuyDatasetPath / SellDatasetPath – 履歴ベクトルを保存するバイナリ ファイル。
- NeighborCount – k-NN 投票で使用される近傍の数。
- CandleType – すべてのインジケーターに使用されるローソク足シリーズ。
使用上の推奨事項
- 分類子モードを有効にする前に、データセット ファイルへの絶対パスまたは作業ディレクトリの相対パスを指定します。
- 履歴データに対してトレーニング モードで戦略を実行し、ベクトルを手動でエクスポートすることで、高品質のサンプルを収集します。
- しきい値と近傍数を最適化して、分類子を新しい市場や商品に適応させます。
- 戦略は必要に応じてネット ポジションを反転するために常に
Volume + |Position| ロットをオープンするため、商品 Volume パラメータをリスク モデルと一致させてください。
MQL4 バージョンとの違い
- 分類子データセットは読み取りのみです。元の EA は初期化解除中に新しいサンプルを書き込みます。ここでは、ユーザーは取引履歴を分析した後、ファイルを手動で更新する必要があります。
- すべての保護命令は、手動の
OrderSend パラメータではなく、StockSharp StartProtection を介して添付されます。
ClosePositionsOnSignal が有効な場合、分類子モードでの注文クローズは常にポジション全体を終了しますが、MQL4 スクリプトは新しいシグナルを取得する前に収益性の高い注文のみをクローズします。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD reversal strategy with moving average ratio filter.
/// Enters on MACD histogram reversals filtered by MA trend alignment.
/// </summary>
public class StatEuclideanMetricStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _trendMaLength;
private readonly List<decimal> _macdHistory = new();
public StatEuclideanMetricStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_fastLength = Param(nameof(FastLength), 12)
.SetDisplay("Fast Length", "Fast EMA period for MACD.", "Indicators");
_slowLength = Param(nameof(SlowLength), 26)
.SetDisplay("Slow Length", "Slow EMA period for MACD.", "Indicators");
_trendMaLength = Param(nameof(TrendMaLength), 50)
.SetDisplay("Trend MA Length", "Period for trend filter MA.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int TrendMaLength
{
get => _trendMaLength.Value;
set => _trendMaLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macdHistory.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var trendMa = new SimpleMovingAverage { Length = TrendMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, trendMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, trendMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue, decimal trendValue)
{
if (candle.State != CandleStates.Finished)
return;
var macdLine = fastValue - slowValue;
_macdHistory.Add(macdLine);
if (_macdHistory.Count > 5)
_macdHistory.RemoveAt(0);
if (_macdHistory.Count < 3)
return;
var close = candle.ClosePrice;
var macd1 = _macdHistory[^1];
var macd2 = _macdHistory[^2];
var macd3 = _macdHistory[^3];
// MACD reversal patterns
var buyReversal = macd3 >= macd2 && macd2 < macd1; // V-shape bottom
var sellReversal = macd3 <= macd2 && macd2 > macd1; // inverted V top
// Exit conditions
if (Position > 0 && sellReversal)
{
SellMarket();
}
else if (Position < 0 && buyReversal)
{
BuyMarket();
}
// Entry conditions with trend filter
if (Position == 0)
{
if (buyReversal && close > trendValue)
{
BuyMarket();
}
else if (sellReversal && close < trendValue)
{
SellMarket();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class stat_euclidean_metric_strategy(Strategy):
"""MACD reversal with trend MA filter."""
def __init__(self):
super(stat_euclidean_metric_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12).SetDisplay("Fast Length", "Fast EMA for MACD", "Indicators")
self._slow_length = self.Param("SlowLength", 26).SetDisplay("Slow Length", "Slow EMA for MACD", "Indicators")
self._trend_ma_length = self.Param("TrendMaLength", 50).SetDisplay("Trend MA Length", "Period for trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "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(stat_euclidean_metric_strategy, self).OnReseted()
self._macd_history = []
def OnStarted2(self, time):
super(stat_euclidean_metric_strategy, self).OnStarted2(time)
self._macd_history = []
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_length.Value
trend_ma = SimpleMovingAverage()
trend_ma.Length = self._trend_ma_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_ema, slow_ema, trend_ma, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, trend_ma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val, trend_val):
if candle.State != CandleStates.Finished:
return
macd_line = fast_val - slow_val
self._macd_history.append(macd_line)
if len(self._macd_history) > 5:
self._macd_history.pop(0)
if len(self._macd_history) < 3:
return
close = float(candle.ClosePrice)
m1 = self._macd_history[-1]
m2 = self._macd_history[-2]
m3 = self._macd_history[-3]
buy_reversal = m3 >= m2 and m2 < m1
sell_reversal = m3 <= m2 and m2 > m1
# Exits
if self.Position > 0 and sell_reversal:
self.SellMarket()
elif self.Position < 0 and buy_reversal:
self.BuyMarket()
# Entries
if self.Position == 0:
if buy_reversal and close > trend_val:
self.BuyMarket()
elif sell_reversal and close < trend_val:
self.SellMarket()
def CreateClone(self):
return stat_euclidean_metric_strategy()