戦略 EMA WMA RSI
概要
EMA WMA RSI は、cmillion によって作成された MetaTrader 4 Expert Advisor「EMA WMA RSI」の変換です。元のロボットは、指数移動平均 (EMA) とローソク足の始値から計算された線形加重移動平均 (WMA) を比較し、すべてのクロスオーバーを相対強度指数 (RSI) のしきい値でフィルター処理します。 StockSharp ポートは同じインジケーター ロジックを保持し、完成したローソク足で動作し、資金管理オプションを再現します。オプションのカウンター ポジションの平坦化、ポイントベースのストップロス/テイクプロフィット レベル、固定距離、最新のフラクタル、または最近のローソク足の極値を追跡できるトレーリング ストップです。
この戦略は、Candle Type パラメーターで選択された単一のシンボルと時間枠向けに設計されています。リスクディスタンスを絶対価格に変換する際には、MetaTrader の「ポイント」(最小ティック)を想定するため、最良の結果を得るには、Security.Step や Security.StepPrice などの商品メタデータを入力する必要があります。
戦略ロジック
インジケーター
- EMA –
EMA Period で定義された期間。ローソク足の始値に適用されます。
- WMA –
WMA Period によって定義される期間。ローソク足の開始値も供給されます。
- RSI –
RSI Period、同じオープン価格ストリームに基づいて計算されます。
すべてのインジケーターは完成したローソクごとに 1 回更新されます。ポートは、前のバーからの EMA/WMA 値を保存し、閉じた直後に現在のバーと比較することで、元の「バーオープン」の実行をミラーリングします。
エントリールール
- 長いセットアップ
- 現在の EMA の値は WMA を下回っていますが、前のバーでは EMA が WMA を上回っていました (下向きのクロス)。
- RSI の値が 50 を超えています。
- ショート ポジションが存在する場合、
Close Counter Trades が有効になっている場合はオプションでクローズされます。それ以外の場合、信号は戦略がフラットになるまで無視されます。
- 条件が満たされると、固定数量またはリスクベースのサイジングを使用して成行買い注文が送信されます。
- 短いセットアップ – 対称ロジック: EMA は WMA を上回っており、前のバーは EMA が WMA を下回っており、RSI は 50 を下回っており、戦略はロングをフラット化するか取引をスキップします。
終了ルール
- 初期保護 –
Stop Loss (points) と Take Profit (points) は、機器のティック サイズを使用して絶対距離に変換されます。どちらの値もゼロに設定して無効にすることができます。
- トレーリングストップ
Trailing Stop (points) がゼロより大きい場合、ストップは最新の終値から測定された固定距離で価格に従います (引き締めのみで、緩めることはありません)。
- 後続距離がゼロの場合、アルゴリズムは適応レベルを検索します。
Trailing Source = CandleExtremes は、過去のローソク足の高値/安値を振り返ります。ロングストップは、現在の価格より少なくとも 5 ポイント低い最初の安値に移動します。ショートストップは5ポイント上の高値を使用します。
Trailing Source = Fractals は、以前に確認された Bill Williams フラクタル (両側に 2 本のキャンドル) をスキャンします。同じ 5 ポイントのバッファーを適用して、ストップが現在の価格に近づきすぎないようにすることができます。
- トレーリング調整は、価格が元のエントリ価格を超えた場合にのみ有効になり、MetaTrader EA の動作が再現されます。
- ポジションの終了 – ローソク足の範囲内でトレーリングストップまたはテイクプロフィットに触れると、ポジションは成行注文でクローズされ、内部状態がリセットされます。
ポジションサイジング
Fixed Volume は、正確な成行注文サイズ (ロット/契約) を提供します。これはデフォルトで、EA パラメータ Lot と一致します。
Fixed Volume をゼロに設定すると、リスクベースのサイジングが有効になります。この戦略は、利用可能なストップ距離 (設定されたストップロスまたは有効トレーリング距離のいずれか) と Security.StepPrice を使用して、ユニットあたりの金銭的リスクを推定します。 Risk % は、取引ごとにポートフォリオの株式がどれだけエクスポージャーされるかを決定します。固定量とリスクパーセントの両方がゼロの場合、信号は無視されます。
パラメーター
| パラメータ |
説明 |
デフォルト |
EMA Period |
ローソク足に適用される指数移動平均の期間が始まります。 |
28 |
WMA Period |
オープン時の線形加重移動平均の期間。 |
8 |
RSI Period |
RSI の長さは方向性フィルターとして使用されます。 |
14 |
Stop Loss (points) |
MetaTrader ポイント単位のストップロス オフセット。 0 は保護停止を無効にします。 |
0 |
Take Profit (points) |
利益確定オフセットをポイント単位で表示します。 0 はターゲットを無効にします。 |
500 |
Trailing Stop (points) |
後続距離をポイント単位で修正しました。 0 は適応トレーリング (フラクタルまたはローソク足の安値/高値) に切り替わります。 |
70 |
Trailing Source |
アダプティブ トレーリング メソッド: 生の高値/安値の場合は CandleExtremes、Williams フラクタルの場合は Fractals。 |
CandleExtremes |
Close Counter Trades |
新しい取引を開始する前に、反対のポジションを閉じてください。 |
false |
Fixed Volume |
成行注文量。リスクベースのサイジングを有効にするには、0 に設定します。 |
0.1 |
Risk % |
Fixed Volume がゼロの場合にコミットされたポートフォリオ資本の割合。有効な停止距離が必要です。 |
10 |
Candle Type |
インジケーターとシグナル評価に使用される主な時間枠。 |
30-minute candles |
実装メモ
- 価格ステップ変換は、
Security.Step (または Security.PriceStep) と Security.StepPrice に依存します。現実的な商品メタデータを提供して、ポイントツープライスの計算を正確に保ちます。
- この戦略は終了したローソク足のみを処理し、その始値をインジケーターの更新に使用し、MQL4 コードの「新しいバー」ロジックと一致します。
- トレーリングレベルは、元のヘルパー関数
SlLastBar と同様に、現在の価格から少なくとも 5 ポイントのバッファーを保ちます。
- カウンターポジションクローズが無効になっている場合、戦略はヘッジを行わず、一度に 1 つのネットポジションのみが管理されます。
- このパッケージには Python 実装は含まれていません。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// EMA WMA RSI: Dual EMA crossover with RSI filter and ATR stops.
/// </summary>
public class EmaWmaRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public EmaWmaRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 8)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 28)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
class ema_wma_rsi_strategy(Strategy):
def __init__(self):
super(ema_wma_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 8) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 28) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(ema_wma_rsi_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 50:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv < 50:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(ema_wma_rsi_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return ema_wma_rsi_strategy()