GitHub で見る
2 MA 他のタイムフレームの正しい交差点戦略
概要
この戦略は、MetaTrader 5 エキスパート アドバイザー「Two MA Other TimeFrame Correct Intersection」の StockSharp 移植です。元の EA は、それぞれ独自の時間枠 (H1 対 D1) で計算された 2 つの移動平均に依存しており、トレードの決定はチャートの時間枠に同期されています。変換ではマルチタイムフレームの動作が維持され、高速移動平均が低速移動平均を上回ったときにロングポジションがオープンされます。逆に、速い平均が遅い平均を下回ると、ショートポジションがオープンされます。すべての注文は市場価格で執行され、この戦略は、MQL5 スクリプトのエンジン主導の実行モデルと一致して、新しい取引を開始する前に常に逆のエクスポージャーをクローズします。
取引ロジック
- プライマリ取引タイムフレーム、高速 MA タイムフレーム、低速 MA タイムフレームの 3 つのローソク足ストリームを購読します。
- 専用の時間枠で高速移動平均と低速移動平均を計算します。各移動平均は、元の
iCustom インジケーターによって公開されたのと同じ平滑化手法と価格ソースをサポートします。
- オプションで、移動平均出力を比較する前に構成可能な水平シフトを適用して、EA の
ma_shift 入力を再現します。
- 主要な取引時間枠のローソク足が終了するたびに、最新の移動平均値と以前の移動平均値の間のクロスオーバーを確認します。
- 前のステップで高速 MA が低速 MA を下回っていたが、現在はそれを上回っている場合は、ショート ポジションを閉じてロング ポジションをオープン (または反転) します。
- 前のステップで高速 MA が低速 MA よりも上にあり、現在はその下にある場合は、ロング ポジションを閉じてショート ポジションをオープン (または反転) します。
- すべてのエントリは、設定された取引量を使用します。既存のポジションを反転する場合、この戦略は、単一の成行注文でポジションが確実に反転するように、反対のエクスポージャーの大きさだけ注文サイズを増やします。
パラメーター
| パラメータ |
説明 |
TradeVolume |
市場参入の基本ボリューム。ロングトレードとショートトレードの両方に使用されます。 |
CandleType |
主要な取引時間枠。このタイプのローソク足が閉じるたびにシグナルが評価されます。 |
FastTimeFrame |
高速移動平均を構築するために使用される時間枠。 |
SlowTimeFrame |
低速移動平均を構築するために使用される時間枠。 |
FastLength |
高速移動平均に含まれるバーの数。 |
SlowLength |
低速移動平均に含まれるバーの数。 |
FastShift |
比較前に高速移動平均出力に適用される水平シフト。 |
SlowShift |
比較の前に低速移動平均出力に適用される水平シフト。 |
FastMethod |
高速移動平均の平滑化アルゴリズム (単純、指数関数、平滑化または線形加重)。 |
SlowMethod |
低速移動平均の平滑化アルゴリズム。 |
FastAppliedPrice |
高速移動平均 (始値、高値、安値、終値、中央値、標準、または加重平均) で使用されるローソク足の価格。 |
SlowAppliedPrice |
低速移動平均で使用されるローソク足の価格。 |
実装メモ
- 移動平均は、StockSharp の高レベルのサブスクリプション (
SubscribeCandles().Bind(...)) を通じて処理され、取引時間枠が計算時間枠と異なる場合でも実行され続けます。
- シフトパラメータは、要求されたバー数だけインジケーターの出力を遅らせる小さなキューで実装され、
ma_shift 入力の動作を複製します。
- この戦略は、オープンポジションを保護する元の取引エンジンと同様に、
StartProtection() を使用して StockSharp アカウント保護ユーティリティと連携します。
- チャートのレンダリングでは、主ローソク足と低速移動平均が追加されるため、バックテスト中にクロスオーバー シグナルが表示されたままになります。
- オリジナルの EA にはストップロス、テイクプロフィット、またはトレーリングストップのモジュールはありません。追加のリスク管理が必要な場合、トレーダーはこのモジュールを別の資金管理戦略と組み合わせることができます。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Dual moving average crossover strategy.
/// Converted from the "Two MA Other TimeFrame Correct Intersection" MQL5 expert advisor.
/// Uses fast and slow SMA on a single timeframe with crossover signals.
/// </summary>
public class TwoMAOtherTimeFrameCorrectIntersectionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private ExponentialMovingAverage _fastMa;
private ExponentialMovingAverage _slowMa;
private decimal? _prevFast;
private decimal? _prevSlow;
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public TwoMAOtherTimeFrameCorrectIntersectionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used for signal evaluation", "General");
_fastLength = Param(nameof(FastLength), 20)
.SetDisplay("Fast MA Length", "Number of bars for the fast moving average", "Indicators")
.SetGreaterThanZero();
_slowLength = Param(nameof(SlowLength), 50)
.SetDisplay("Slow MA Length", "Number of bars for the slow moving average", "Indicators")
.SetGreaterThanZero();
}
/// <summary>
/// Primary candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of bars used by the fast moving average.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Number of bars used by the slow moving average.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
_fastMa = new ExponentialMovingAverage { Length = FastLength };
_slowMa = new ExponentialMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Fast crosses above slow => buy
if (_prevFast < _prevSlow && fastValue > slowValue)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
// Fast crosses below slow => sell
else if (_prevFast > _prevSlow && fastValue < slowValue)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevFast = null;
_prevSlow = null;
_fastMa = null;
_slowMa = 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
from StockSharp.Algo.Strategies import Strategy
class two_ma_other_time_frame_correct_intersection_strategy(Strategy):
def __init__(self):
super(two_ma_other_time_frame_correct_intersection_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._fast_length = self.Param("FastLength", 20)
self._slow_length = self.Param("SlowLength", 50)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastLength(self):
return self._fast_length.Value
@FastLength.setter
def FastLength(self, value):
self._fast_length.Value = value
@property
def SlowLength(self):
return self._slow_length.Value
@SlowLength.setter
def SlowLength(self, value):
self._slow_length.Value = value
def OnReseted(self):
super(two_ma_other_time_frame_correct_intersection_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(two_ma_other_time_frame_correct_intersection_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.FastLength
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.SlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ma, slow_ma, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if self._has_prev:
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
self._has_prev = True
def CreateClone(self):
return two_ma_other_time_frame_correct_intersection_strategy()