GitHub で見る
ダイバージェンス + EMA + RSI 閉じる購入のみ
概要
この戦略は、MetaTrader の「ダイバージェンス + ema + rsi 終値買いのみ」エキスパート アドバイザーを StockSharp の高レベルの API に移植します。 5 分足ローソクに基づいて機能し、時間 および 日 データを参照して、トレンドの一致と売られ過ぎの状態を確認します。オーダーはロングのみとなります。エントリーには、強気の MACD ヒストグラムの発散が必要です。これは、タイトな売られすぎバンド内の 1 時間ごとの確率的クロスオーバーと、日次の上昇する EMA 構造によって確認されます。イグジットは、フレームワークによって管理されるオプションのストップロスおよびテイクプロフィット保護と組み合わせた固定の RSI オーバーシュートに依存します。
取引ロジック
より長い時間枠のトレンド フィルター
- 上昇トレンドを確実にするには、日次 EMA(9) が EMA(20) を上回る必要があります。
- 直近の5分間終値は日足のEMA(9)を下回って、戻りからのロングエントリーが試みられるようにする必要があります。
時間ごとの確率的確認
- 最新の完了した時間ごとの確率的 %K 値は、
StochasticLowerBound (デフォルト 0) と StochasticUpperBound (デフォルト 40) の間になければなりません。
- %K は最後の時間足で %D を上回っている必要があります (現在の %K > %D で、前の %K ≤ 以前の %D)。
MACD 分岐トリガー (5 分)
- MACD ヒストグラム (MACD ラインからシグナル ラインを引いたもの) は、5 分足の終値が前のローソク足と比較して低い安値を設定する間に、少なくとも
MacdThreshold 改善する必要があります。これは、元の EA で使用された強気ダイバージェンスに近似します。
エントリー実行
- すべてのフィルターが一致し、ロングポジションがない場合、戦略は市場買いを送信します。予期しないショートポジションが存在する場合は、ロングを反転する前に、要求されたボリュームが増加してそれを中和します。
退出ルール
- 保護的な RSI 出口は、5 分足 RSI が
RsiExitLevel (デフォルト 77) を超えたときにロングを閉じます。
StartProtection は、対応するパラメータが正の場合は常に、ピップから価格距離に変換されたストップロスとテイクプロフィットの両方のレベルをアクティブにします。
注文管理
- 約定の重複を避けるため、新しい成行買い注文を送信する前に、アクティブな注文はすべてキャンセルされます。
- 音量のデフォルトは
TradeVolume パラメータで、最適化のために調整できます。
パラメーター
| 名前 |
説明 |
デフォルト |
CandleType |
MACD、RSI、および実行の主なタイムフレーム。 |
5分キャンドル |
HourTimeFrame |
確率的フィルターで使用される時間枠。 |
1時間 |
DayTimeFrame |
EMA のトレンド確認の日次時間枠。 |
1日 |
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod |
プライマリタイムフレームの MACD 構造。 |
6/13/5 |
MacdThreshold |
発散を受け入れるための最小の MACD ヒストグラムの増加。 |
0.0003 |
DailyFastPeriod / DailySlowPeriod |
毎日の EMA 期間。 |
9/20 |
StochasticKPeriod / StochasticDPeriod / StochasticSlowing |
時間ごとの確率的構成。 |
30/5/9 |
StochasticUpperBound / StochasticLowerBound |
許容される時間単位の %K 範囲。 |
40/0 |
RsiPeriod |
プライマリ タイムフレームの長さは RSI です。 |
7 |
RsiExitLevel |
RSI の値は強制的に長時間終了します。 |
77 |
TradeVolume |
購入の基本注文サイズ。 |
0.01 |
StopLossPips |
ストップロス距離 (ピップ単位) (0 は無効)。 |
100 |
TakeProfitPips |
利益確定距離 (ピップ単位) (0 を無効にします)。 |
200 |
注意事項
- この戦略は、構成されたプライマリ時間枠、時間単位のシリーズ、日単位のシリーズという 3 つのデータ ストリームをサブスクライブします。各ストリームは、実装を簡潔かつイベント駆動型に保つために、
Bind/BindEx を介して独自のインジケーター セットを駆動します。
- インジケーター値は、元の EA のシフト パラメーターを反映するために、完成したキャンドルでのみ処理されます。
- MACD 発散検出では、ソース MQL ファイルからビルダーが生成したロジックのシンプルかつ堅牢な近似値として、前のバーの終値とヒストグラム値を使用します。
- ストップロスとテイクプロフィットは、ブローカーの約定との同期を維持するために
StartProtection によって処理され、手動で注文を複製することなくバックテストやライブ取引をサポートします。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Divergence EMA RSI Close Buy Only" MetaTrader expert.
/// Long-only strategy: buys when price pulls back below fast EMA with RSI oversold,
/// exits when RSI reaches overbought level.
/// </summary>
public class DivergenceEmaRsiCloseBuyOnlyStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiEntry;
private readonly StrategyParam<decimal> _rsiExit;
private ExponentialMovingAverage _ema;
private RelativeStrengthIndex _rsi;
private decimal? _prevRsi;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal RsiEntry
{
get => _rsiEntry.Value;
set => _rsiEntry.Value = value;
}
public decimal RsiExit
{
get => _rsiExit.Value;
set => _rsiExit.Value = value;
}
public DivergenceEmaRsiCloseBuyOnlyStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signals", "General");
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for trend filter", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period for entry/exit", "Indicators");
_rsiEntry = Param(nameof(RsiEntry), 35m)
.SetDisplay("RSI Entry", "RSI level to enter long", "Signals");
_rsiExit = Param(nameof(RsiExit), 65m)
.SetDisplay("RSI Exit", "RSI level to exit long", "Signals");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_prevRsi = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, _rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_rsi.IsFormed)
{
_prevRsi = rsiValue;
return;
}
if (_prevRsi is null)
{
_prevRsi = rsiValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var close = candle.ClosePrice;
// Exit: RSI crosses above exit level
if (Position > 0 && _prevRsi.Value < RsiExit && rsiValue >= RsiExit)
{
SellMarket(Position);
}
// Entry: RSI crosses below entry level and price is near/below EMA (buy the dip)
if (Position <= 0 && _prevRsi.Value > RsiEntry && rsiValue <= RsiEntry && close <= emaValue * 1.005m)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(volume);
}
_prevRsi = rsiValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_ema = null;
_rsi = null;
_prevRsi = null;
base.OnReseted();
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class divergence_ema_rsi_close_buy_only_strategy(Strategy):
def __init__(self):
super(divergence_ema_rsi_close_buy_only_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._ema_period = self.Param("EmaPeriod", 20)
self._rsi_period = self.Param("RsiPeriod", 7)
self._rsi_entry = self.Param("RsiEntry", 35.0)
self._rsi_exit = self.Param("RsiExit", 65.0)
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def RsiEntry(self):
return self._rsi_entry.Value
@RsiEntry.setter
def RsiEntry(self, value):
self._rsi_entry.Value = value
@property
def RsiExit(self):
return self._rsi_exit.Value
@RsiExit.setter
def RsiExit(self, value):
self._rsi_exit.Value = value
def OnReseted(self):
super(divergence_ema_rsi_close_buy_only_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(divergence_ema_rsi_close_buy_only_strategy, self).OnStarted2(time)
self._prev_rsi = None
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._process_candle).Start()
def _process_candle(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
rsi_val = float(rsi_value)
close = float(candle.ClosePrice)
if self._prev_rsi is None:
self._prev_rsi = rsi_val
return
rsi_entry = float(self.RsiEntry)
rsi_exit = float(self.RsiExit)
# Exit: RSI crosses above exit level
if self.Position > 0 and self._prev_rsi < rsi_exit and rsi_val >= rsi_exit:
self.SellMarket()
# Entry: RSI crosses below entry level and price is near/below EMA
if self.Position <= 0 and self._prev_rsi > rsi_entry and rsi_val <= rsi_entry and close <= ema_val * 1.005:
self.BuyMarket()
self._prev_rsi = rsi_val
def CreateClone(self):
return divergence_ema_rsi_close_buy_only_strategy()