GitHub で見る
EMA RSI ボラティリティ適応クロスオーバー戦略
この戦略は、MetaTrader エキスパート アドバイザー EA_MARSI_1-02 の直接移植です。の2つのコピー間のクロスオーバーを交換します。
Integer のカスタム EMA_RSI_VA インジケーター、相対強度指数 (RSI) によって駆動されるボラティリティ適応型移動平均。
低速ラインが高速ラインを横切るたびに、エンジンはネット位置を逆転させ、元の「フリップオンクロスオーバー」を再現します。
StockSharp の注文処理のベスト プラクティスを尊重しながらの行動。
インジケーターの仕組み
オリジナルの MQL パッケージには、EMA_RSI_VA というカスタム インジケーターが同梱されています。価格平滑化された EMA を計算します。
長さは、中立値からの RSI の距離によって調整されます。 StockSharp ポートは、
数式を正確に複製する EmaRsiVolatilityAdaptiveIndicator クラス:
- 選択した
AppliedPrice ソースの RSI を期間 RSIPeriod で計算します。
- 50 (
|RSI - 50| + 1) からの RSI の距離を測定します。これはボラティリティのプロキシとして機能します。
- 適応乗算器を導出する
multi = (5 + 100 / RSIPeriod) / (0.06 + 0.92 * dist + 0.02 * dist^2)。
- 構成された EMA 期間にこの乗数を乗じて、動的な長さ
pdsx を取得します。
- ローソク足の適用価格を入力として使用し、平滑化係数
2 / (pdsx + 1) を使用して標準の EMA 再帰を適用します。
RSI の偏位が大きいと、スムージング ウィンドウが短くなり、ラインの反応が速くなります。フラットな RSI は窓を長くし、湿気を軽減します
騒音。低速回線と高速回線の両方で、StockSharp.Messages.AppliedPrice でサポートされる価格モードの完全なセットが公開されます。
取引ルール
- 信号検出
- 売り/空売り: 以前のスロー < 以前の速い ** および ** 現在のスロー ≧ 現在の速い。
- 買い/ロング: 以前のスロー > 以前のファースト ** および ** 現在のスロー ≤ 現在のファースト。
- 実行
- この戦略は、構成されたキャンドル シリーズから完成したキャンドルのみを分析します。
- シグナルが発生すると、既存のエクスポージャーをクローズし、新しい方向を開くためのサイズの成行注文が送信されます。
- 交換制限は、
Security.MinVolume、Security.VolumeStep、および Security.MaxVolume を通じて尊重されます。
- 逆転
- 注文はネッティングされるため、単一の
SellMarket または BuyMarket 呼び出しがゼロラインを越えてポジションを取得し、
MQL 反対のシグナルが即座に取引を反転させる動作。
リスク管理
TakeProfitPoints と StopLossPoints は、エキスパート アドバイザーの TP/SL フィールド (価格ポイントで表現) を複製します。どちらかのとき
値がゼロ以外の場合、戦略は絶対価格オフセットと useMarketOrders = true を使用して StockSharp の保護マネージャーを開始します。
元の OrderSend ストップ/リミット変更ループをミラーリングします。
UseBalanceMultiplier は use_Multpl トグルを実装します。有効な場合、有効注文量は次のようになります。
Volume * PortfolioEquity / MaxDrawdown には、制約を交換するための防御クランプが付いています。
- 基本クラスの
StartProtection() 呼び出しは引き続き実行されるため、外部リスク モジュールがトレーリングまたは損益分岐点を付加できるようになります。
必要に応じてロジックを変更します。
パラメーター
| パラメータ |
デフォルト |
説明 |
Volume |
0.1 |
残高乗数が適用される前のベース成行注文サイズ。 |
TakeProfitPoints |
0 |
商品ポイントでの利食い距離。 0 はテイクプロフィットレッグを無効にします。 |
StopLossPoints |
0 |
計器ポイントでのストップロス距離。 0 は保護停止を無効にします。 |
UseBalanceMultiplier |
false |
EA の use_Multpl と同じ残高比例ポジションサイジングを有効にします。 |
MaxDrawdown |
10000 |
バランス乗数の分母。 EA の Max_drawdown に対応します。 |
SlowRsiPeriod |
310 |
RSI は遅い EMA_RSI_VA 回線をルックバックします。 |
SlowEmaPeriod |
40 |
RSI を適応させる前の低速回線の基本 EMA の長さ。 |
SlowAppliedPrice |
Close |
価格モードは低速インジケーターに転送されます。 |
FastRsiPeriod |
200 |
RSI は高速な EMA_RSI_VA 行をルックバックします。 |
FastEmaPeriod |
50 |
RSI を適応させる前の高速回線の基本 EMA の長さ。 |
FastAppliedPrice |
Close |
価格モードは高速インジケーターに転送されます。 |
CandleType |
TimeFrame(1m) |
計算に使用されるローソク足シリーズ。 |
実装メモ
- ポートは、手動インジケーターのループを避けるために、StockSharp の高レベルの API (
SubscribeCandles().Bind(...)) で書き込まれます。
- MQL ソース内の
CopyBuffer(..., 1, 2, ...) 呼び出しと一致する、完了したキャンドルのみが処理されます。
- ボリュームの正規化では、
Security.MinVolume、Security.VolumeStep、および Security.MaxVolume を使用し、無効な注文を防ぎます。
本当のやりとり。
- Python バージョンは、要求に応じて意図的に省略されています。このディレクトリには C# 実装とドキュメントのみが含まれます。
結果として得られる動作は、ソース EA を反映しながら、StockSharp に適したパラメータとリスク制御を公開します。
デザイナー、ランナー、または StockSharp API 上に構築されたカスタム ホスト。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// EMA RSI VA Crossover: Fast/slow EMA crossover with RSI volatility filter.
/// </summary>
public class EmaRsiVaCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public EmaRsiVaCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 40)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI 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;
}
/// <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 = 14 };
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 (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (fastVal < slowVal && _prevFast >= _prevSlow))
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (fastVal > slowVal && _prevFast <= _prevSlow))
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 40 && rsiVal < 70)
{
_entryPrice = close;
BuyMarket();
}
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal > 30 && rsiVal < 60)
{
_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_rsi_va_cross_strategy(Strategy):
def __init__(self):
super(ema_rsi_va_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 40) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI 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
def OnStarted2(self, time):
super(ema_rsi_va_cross_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 = 14
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 close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 2.0 or (fv < sv and self._prev_fast >= self._prev_slow):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 2.0 or (fv > sv and self._prev_fast <= self._prev_slow):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 40 and rv < 70:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv > 30 and rv < 60:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(ema_rsi_va_cross_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return ema_rsi_va_cross_strategy()