外国為替スカイ戦略
概要
Forex Sky Strategy は、MetaTrader エキスパート アドバイザー Forex_SKY.mq4 の直接移植です。 MACD のモメンタム スイングを取引し、取引日あたり 1 つのポジションに厳密に制限されます。 StockSharp の実装では、元の MACD のしきい値と、キャンドルごとに複数の注文を防止する安全チェックが維持されます。
この戦略は、CandleType (デフォルトでは 15 分のローソク足) で定義されたタイムフレームをサブスクライブし、完了した各ローソク足の終値で古典的な MACD (12/26/9) を評価します。
取引ロジック
- ロングエントリー – 次の場合に成行買いを行います。
- 現在の MACD の主線はゼロより上です。
- また、
+0.00009 を超えており、勢いが確認されています。
- 前の 3 つの MACD 測定値のうち少なくとも 1 つはゼロ以下でした (マイナス領域から強気反転を捉えた)。
- ショートエントリー – 以下のいずれかに該当する場合、成行売りを出します。
- MACD の主線はゼロを下回り、
-0.0004 を下回っており、最後の 3 つの測定値のうち少なくとも 1 つは負ではなく、4 バー前の値は少なくとも +0.001 でした。
- または 4 足前の値は
≥ +0.003 で、元の MetaTrader コードと同様に空売り取引が即座に承認されます。
- ポジション管理 – このアルゴリズムは、ローソクごとに複数の注文をオープンすることはなく (
Time0 ガード)、暦日に複数回取引することもありません (CheckTodaysOrders ガード)。保護的出口命令は StockSharp StartProtection ヘルパーによって処理されるため、すべてのストップとターゲットは現在のボリュームと同期されたままになります。
保護命令を超える自律的なフラット化ロジックはありません。ポジションは、元のエキスパートアドバイザーの動作を反映して、テイクプロフィット、ストップロス、または手動介入によってクローズされることが期待されます。
パラメーター
| 名前 |
デフォルト |
説明 |
FastPeriod |
12 |
MACD インジケーターの高速な EMA 長さ。 |
SlowPeriod |
26 |
MACD インジケーターの EMA の長さが遅いです。 |
SignalPeriod |
9 |
MACD インジケーターの EMA の長さを信号します。 |
TakeProfitPoints |
100 |
商品ポイントで表される利食い注文までの距離。証券価格ステップを乗じて価格に変換されます。 |
StopLossPoints |
3000 |
商品ポイント単位でのストップロス注文までの距離。 |
TradeVolume |
0.1 |
基本成行注文サイズ (ロット)。 |
CandleType |
15分の時間枠 |
MACD の計算と取引の決定を行うタイムフレーム。 |
器械点計算
TakeProfitPoints と StopLossPoints は、MetaTrader バージョンとまったく同じように指定されます。MQL4 の Point は、StockSharp の Security.PriceStep に対応します。 5 桁の外国為替ペア (PriceStep = 0.00001) の場合、デフォルト設定は次のように変換されます。
- テイクプロフィット:
100 × 0.00001 = 0.001 価格単位。
- ストップロス:
3000 × 0.00001 = 0.03 価格単位。
リスク管理
StartProtection は、エントリーが約定された後、テイクプロフィット注文とストップロス注文を自動的にインストールします。これらは取引方向にリンクされており、トリガーされると成行注文を使用し、MetaTrader の動作と一致します。対応する保護命令を無効にするには、いずれかのパラメータを 0 に設定します。
移行メモ
- MACD 履歴バッファはクラス フィールド内の最後の 4 つの完了値を保持するため、シフトされたインデックスを使用したインジケーター呼び出しは必要ありません。
- 日次取引の調整とバーごとの単一取引の制約により、元のソースから
CheckTodaysOrders() と Time0 が複製されます。
- すべてのコメントは英語で書き直され、ロジックはインジケーター処理のために StockSharp の高レベル バインディング (
Bind) に依存しています。
使用のヒント
CandleType をエミュレートしたいチャート期間に合わせて調整します。元のスクリプトはチャートの時間枠を自動的に継承します。
- 許可される取引は 1 日に 1 回のみであるため、日中の変動が大きい市場を選択するか、ボラティリティの高い商品を使用する場合は MACD のしきい値を上げることを検討してください。
- ローソク足の開始日に基づいてリミットカウンターがリセットされるため、プラットフォームのクロック/タイムゾーンを監視して、日の境界が取引セッションと一致していることを確認します。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Forex Sky: MACD zero-line cross with EMA trend filter.
/// </summary>
public class ForexSkyStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevMacd;
private decimal _entryPrice;
public ForexSkyStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastLength = Param(nameof(FastLength), 12)
.SetDisplay("Fast EMA", "MACD fast period.", "Indicators");
_slowLength = Param(nameof(SlowLength), 26)
.SetDisplay("Slow EMA", "MACD slow period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = 0;
_entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var macd = fastVal - slowVal;
var close = candle.ClosePrice;
if (_prevMacd == 0)
{
_prevMacd = macd;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (macd < 0 && _prevMacd >= 0))
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (macd > 0 && _prevMacd <= 0))
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (macd > 0 && _prevMacd <= 0 && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (macd < 0 && _prevMacd >= 0 && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevMacd = macd;
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class forex_sky_strategy(Strategy):
"""
Forex Sky: MACD zero-line cross with EMA trend filter and ATR-based exits.
"""
def __init__(self):
super(forex_sky_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast EMA", "MACD fast period", "Indicators")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow EMA", "MACD slow period", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._prev_macd = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(forex_sky_strategy, self).OnReseted()
self._prev_macd = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(forex_sky_strategy, self).OnStarted2(time)
self._prev_macd = 0.0
self._entry_price = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, ema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_val)
slow_val = float(slow_val)
ema_val = float(ema_val)
atr_val = float(atr_val)
if atr_val <= 0:
return
macd = fast_val - slow_val
close = float(candle.ClosePrice)
if self._prev_macd == 0:
self._prev_macd = macd
return
if self.Position > 0:
if (close >= self._entry_price + atr_val * 3.0 or
close <= self._entry_price - atr_val * 2.0 or
(macd < 0 and self._prev_macd >= 0)):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (close <= self._entry_price - atr_val * 3.0 or
close >= self._entry_price + atr_val * 2.0 or
(macd > 0 and self._prev_macd <= 0)):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if macd > 0 and self._prev_macd <= 0 and close > ema_val:
self._entry_price = close
self.BuyMarket()
elif macd < 0 and self._prev_macd >= 0 and close < ema_val:
self._entry_price = close
self.SellMarket()
self._prev_macd = macd
def CreateClone(self):
return forex_sky_strategy()