GitHub で見る
クロス戦略 (MQL 27596 コンバージョン)
概要
クロス戦略は、MetaTrader エキスパート アドバイザー Cross.mq4 (リポジトリ エントリ MQL/27596) の直接変換です。オリジナルの EA は、足の始値で測定された単一の指数移動平均 (EMA) クロスを取引し、固定距離のテイクプロフィットとストップロスのレベルを適用しました。この StockSharp ポートは、ローソク足サブスクリプション、インジケーター バインディング、マネージド ポジション トラッキングなどの高レベルの API 機能を使用しながら、取引ロジックをそのまま維持します。
取引ロジック
- インジケーター – ローソク足の終値から計算された単一の指数移動平均 (EMA)。期間は構成可能で、デフォルトは 200 で、MQL ソースと一致します。
- シグナル検出 – 完成したローソク足ごとに、この戦略はオープンしたローソク足と EMA の値を比較します。
- 強気シグナルは、ローソク足が EMA またはそれ以下でオープンした後、その上でオープンしたときに発生します。これにより、MQL スクリプト内の
Cross(0, Open[0] > EMA) 呼び出しが再現されます。
- 弱気シグナルは、ローソク足が EMA 以上でオープンした後、その下でオープンしたときに発生します (元のコードでは
Cross(1, Open[0] < EMA))。
- ポジション管理 – シグナルが発火すると、戦略は現在のポジションを完全に逆転させます。
- フラットまたはショートのときに強気のクロスが現れた場合、ショートのエクスポージャーをカバーし、新しいロングポジションをオープンするのに十分なボリュームを購入します。
- フラットまたはロングの間に弱気のクロスが現れた場合、ロングエクスポージャーをフラットにしてショートポジションを確立するのに十分な量が売られています。
- リスクコントロール – ポジションを入力した後、戦略はローソク足の高値と安値を監視して、価格ステップ単位で固定テイクプロフィットとストップロスエグジットを実装します。これらの出口は、MetaTrader の
TakeProfit と StopLoss の両方を設定する OrderSend 呼び出しをエミュレートします。
パラメーター
| パラメータ |
デフォルト |
説明 |
EMA Length |
200 |
クロス検出に使用される EMA の期間。ゼロより大きくなければなりません。 |
Take Profit (steps) |
200 |
価格ステップで測定されたテイクプロフィットレベルまでの距離。利益目標を無効にするには、ゼロに設定します。 |
Stop Loss (steps) |
100 |
価格ステップで測定された保護ストップまでの距離。停止を無効にするには、ゼロに設定します。 |
Candle Type |
1分の時間枠 |
ストラテジーによって処理されたキャンドル データ ソース。 StockSharp でサポートされている他の時間枠やカスタムのローソク足タイプに切り替えることができます。 |
取引量は戦略の Volume プロパティによって制御されます。反転シグナルが到着すると、ストラテジーは Volume + |Position| を送信して、新しいポジションを開く前に既存のエクスポージャーが閉じられていることを確認します。
実行フロー
OnStarted は、設定されたローソク足シリーズをサブスクライブし、高レベルの Bind ヘルパーを使用して EMA インジケーターをバインドします。
- ハンドラーは未完成のキャンドルをスキップし、EMA が完全に形成されるまで待ちます。準備ができたら、次のようになります。
- ローソク足の高値/安値に対してストップロスとテイクプロフィットのレベルをチェックすることで、アクティブなポジションを管理します。
- EMA に対するローソク足の始値に基づいて強気クロスと弱気クロスを検出します。
- 新しいシグナルが現れたときに、ポジションを逆転させる成行注文を発行します。
OnNewMyTrade は、平均エントリー価格とアクティブなポジションの方向を追跡するため、取引を拡大する場合でも出口チェックで正確なレベルが使用されます。
- オプションのチャート オブジェクトが作成され (チャートが使用可能な場合)、ローソク足、EMA ライン、および実行された取引が表示されます。
リスク管理の詳細
- ストップロス – 方向に応じて
entry price ± stop steps × price step として計算されます。この戦略は、ローソク足の安値 (ロング) または高値 (ショート) がストップレベルを突破すると、直ちに終了します。
- 利益獲得 – 設定された利益ステップを使用して同様に計算されます。ターゲットにヒットすると、高値/安値がしきい値を横切るローソク足の間にポジション全体が決済されます。
- アカウント保護 –
StartProtection() は起動時に 1 回呼び出されるため、戦略は StockSharp 環境で構成されたグローバル保護ルールを尊重します。
カスタマイズのヒント
- タイムフレームまたは EMA の長さが短いほど、反転の頻度が高くなります。停止距離を長くすることと組み合わせると、ホイップソーを回避できます。
- 複数のシンボルを取引するには、独自の証券およびキャンドル タイプを使用して個別のストラテジー インスタンスをインスタンス化します。
- 最適化するときは、EMA の長さを維持し、商品のボラティリティとティック サイズの現実的な範囲内で停止/距離を設定してください。
変換メモ
- MQL 配列
crossed[2] は、ローソク足全体で持続する 2 つの内部ブール フラグにマッピングされます。
- MQL
OrderSend 関数は、StockSharp の BuyMarket および SellMarket ヘルパーによって表され、反転と新しいエントリの両方が元の動作を確実に反映します。
- EMA 値はバインド コールバックを通じて提供され、リポジトリ ガイドラインで要求されている直接の
GetValue 呼び出しを回避します。
これらの詳細に従うことで、データ ソース、パラメータの最適化、グラフ作成を完全に制御しながら、元の MetaTrader 戦略を StockSharp 内で再現できます。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Cross strategy: opens long when candle open crosses above EMA,
/// short when candle open crosses below EMA.
/// </summary>
public class CrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _signalCooldownCandles;
private bool _prevAbove;
private bool _hasPrev;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public CrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaLength = Param(nameof(EmaLength), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period for cross detection", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 3)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAbove = false;
_hasPrev = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
_candlesSinceTrade = SignalCooldownCandles;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var above = candle.OpenPrice > ema && candle.ClosePrice > ema;
if (_hasPrev && _candlesSinceTrade >= SignalCooldownCandles)
{
if (above && !_prevAbove && Position <= 0)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (!above && _prevAbove && Position >= 0)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevAbove = above;
_hasPrev = true;
}
}
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 cross_strategy(Strategy):
def __init__(self):
super(cross_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 100) \
.SetDisplay("EMA Length", "EMA period for cross detection", "Indicators")
self._signal_cooldown = self.Param("SignalCooldownCandles", 3) \
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading")
self._ema = None
self._prev_above = False
self._has_prev = False
self._candles_since_trade = 0
@property
def ema_length(self):
return self._ema_length.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(cross_strategy, self).OnReseted()
self._ema = None
self._prev_above = False
self._has_prev = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(cross_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_length
self._has_prev = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(15)))
subscription.Bind(self._ema, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
ema_val = float(ema_value)
above = float(candle.OpenPrice) > ema_val and float(candle.ClosePrice) > ema_val
if self._has_prev and self._candles_since_trade >= self.signal_cooldown:
if above and not self._prev_above and self.Position <= 0:
self.BuyMarket()
self._candles_since_trade = 0
elif not above and self._prev_above and self.Position >= 0:
self.SellMarket()
self._candles_since_trade = 0
self._prev_above = above
self._has_prev = True
def CreateClone(self):
return cross_strategy()