GitHub で見る
2 つの EMA 日中フィルター戦略
概要
この戦略は、StockSharp の高レベル API を使用して、MetaTrader エキスパートアドバイザー Expert_2EMA_ITF を再現します。 2 つの指数移動平均のクロスオーバーで取引され、平均真のレンジ (ATR) を使用して未決の指値注文、保護ストップ、ターゲットを設定します。追加の日中時間フィルターは、望ましくない分、時間、または曜日のエントリをブロックします。
ロジック概要
- 選択したローソク足シリーズの高速および低速の EMA 値を計算します。
- 速い EMA が遅い EMA を上回ると強気のクロスオーバーを検出し、下回ると弱気のクロスオーバーを検出します。
- 強気のクロスオーバーでは、遅い EMA から
LimitMultiplier * ATR に現在のスプレッドを加えたオフセットで買い指値注文を出します。弱気のクロスオーバーでは、反対方向に売りの指値注文オフセットを配置します。
- ATR 乗数を使用してストップロスとテイクプロフィットの価格を保存し、エントリー注文が約定したらすぐに送信できるようにします。
- 未約定注文が
ExpirationBars キャンドルを超えて約定されていない場合、自動的にキャンセルされます。
- 日中フィルターを通過しない信号をスキップします (分、時間、および日のチェックが許可されます)。ビット マスクを使用すると、複数の分、時間、または日を同時に無効にすることができます。
インジケーター
- Fast EMA – クロスオーバー検出の感度を制御します。
- スロー EMA – トレンドの方向を定義します。
- 平均トゥルー レンジ (ATR) – 市場のボラティリティを測定し、エントリー/エグジット価格のオフセットをスケールします。
パラメーター
| パラメータ |
説明 |
デフォルト |
CandleType |
計算に使用される時間枠。 |
30分キャンドル |
FastEmaPeriod |
高速の EMA の期間。 |
5 |
SlowEmaPeriod |
遅い期間 EMA (速い期間より長くなければなりません)。 |
30 |
AtrPeriod |
ATR の計算期間。 |
7 |
LimitMultiplier |
制限エントリー価格をシフトする ATR 乗数。 |
1.2 |
StopLossMultiplier |
ストップロスの配置に対する ATR の乗数。 |
5 |
TakeProfitMultiplier |
テイクプロフィットプレースメントの乗数は ATR です。 |
8 |
ExpirationBars |
未約定注文がキャンセルされるまでのバーの数。 |
4 |
GoodMinuteOfHour |
エントリに許可される特定の分 (-1 は無効)。 |
-1 |
BadMinutesMask |
分をブロックするビット マスク (ビット n は分 n をブロックします)。 |
0 |
GoodHourOfDay |
エントリに許可される特定の時間 (-1 は無効になります)。 |
-1 |
BadHoursMask |
ビット マスクは時間をブロックします (ビット n は時間 n をブロックします)。 |
0 |
GoodDayOfWeek |
エントリに許可される特定の日 (-1 は無効、0 = 日曜日)。 |
-1 |
BadDaysMask |
日をブロックするビット マスク (ビット n は日 n をブロック、0 = 日曜日)。 |
0 |
注文管理
- エントリー注文 – 指値注文は、遅い EMA から ATR ベースのオフセットだけシフトされた価格で登録されます。買値/売値が利用可能な場合、買い注文には現在のスプレッドも追加されます。
- 有効期限 – 各未決注文には、作成時のローソク足インデックスが保存されます。
ExpirationBars がプラスであり、注文がその数の足を超えて存続する場合、注文は自動的にキャンセルされます。
- 保護注文 – エントリー注文が約定すると、戦略はそれまでのストップ/ターゲット注文をキャンセルし、シグナルを生成した ATR スナップショットから計算されたストップロスとテイクプロフィットを即座に配置します。ポジションが横ばいの場合、反対の保護注文はキャンセルされます。
日中フィルターの詳細
- 単一の許可値 –
GoodMinuteOfHour、GoodHourOfDay、および GoodDayOfWeek は、負でない場合、取引を特定の分、時間、または平日に制限します。
- ビット マスク –
BadMinutesMask、BadHoursMask、および BadDaysMask には、複数のタイムスロットを一度に無効にするビットが含まれています。たとえば、BadMinutesMask = (1 << 0) | (1 << 30) を設定すると、各時間の 0 分と 30 分の取引がブロックされます。
- 組み合わせロジック – 現在のローソク足時間がすべての許可条件を通過し、どのマスクもブロックしていない場合にのみ、エントリーが許可されます。
元のExpert Advisorとの違い
- StockSharp バージョンでは、エントリーが実行されると、保留中の指値注文と明示的なストップロスおよびテイクプロフィット登録を組み合わせて使用し、MQL シグナル計算を反映します。
- 買い注文のスプレッド補償では、利用可能な場合は現在の
Security.BestBid/BestAsk 相場が使用され、それ以外の場合はオフセットがゼロになります。
- 時間フィルタリングは、MetaTrader 固有の時間フィルタ ヘルパー クラスではなく、ビット マスクと直接比較を通じて表現されます。
- すべての取引アクションは、手動注文配列の代わりに、StockSharp の高レベル ヘルパー (
BuyLimit、SellLimit、SellStop、BuyStop) と自動キャンセル ロジックを活用します。
使用上の注意
- 戦略を開始する前に、戦略ボリュームが設定されていることを確認してください。それ以外の場合は、警告が生成され、注文は送信されません。
- 最適化シナリオでは、パラメーター メタデータにより、EMA 期間、ATR 期間、乗数、有効期限の長さの調整がすでに有効になっています。
- この戦略では、ローソク足の終了時刻がバーの終わりを表すと想定し、日中フィルターを評価するときにそれらの時刻を使用します。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Two EMA Intraday Filter strategy: EMA crossover.
/// Buys when fast EMA crosses above slow EMA. Sells on cross below.
/// </summary>
public class TwoEmaIntradayFilterStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public TwoEmaIntradayFilterStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
decimal? prevFast = null;
decimal? prevSlow = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, (candle, fastVal, slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevFast.HasValue && prevSlow.HasValue)
{
if (prevFast.Value <= prevSlow.Value && fastVal > slowVal && Position <= 0)
BuyMarket();
else if (prevFast.Value >= prevSlow.Value && fastVal < slowVal && Position >= 0)
SellMarket();
}
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
}
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
from StockSharp.Algo.Strategies import Strategy
class two_ema_intraday_filter_strategy(Strategy):
def __init__(self):
super(two_ema_intraday_filter_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._fast_period = self.Param("FastPeriod", 12) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetGreaterThanZero() \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(two_ema_intraday_filter_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(two_ema_intraday_filter_strategy, self).OnStarted2(time)
self._fast_ind = ExponentialMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = ExponentialMovingAverage()
self._slow_ind.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if self._prev_fast is not None and self._prev_slow is not None:
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return two_ema_intraday_filter_strategy()