GitHub で見る
Parabolic SAR 制限
Parabolic SAR 制限は、MT4 エキスパート アドバイザー ytg_Parabolic_exp.mq4 の直接ポートです。システムは継続的に売買の指値注文を Parabolic SAR の値に固定し、市場が注文を取引に引き込めるようにします。満たされると、この戦略はオープンポジションを管理し、元の MQL の動作を反映して、ローソク足の極値を使用してストップロスまたはテイクプロフィットのエグジットを実行します。
戦略ロジック
- この戦略は、設定可能なローソク足シリーズ (デフォルトでは 4 時間の時間枠) をサブスクライブし、MT4 スクリプトと同じステップと最大値で Parabolic SAR インジケーターを計算します。
- 完成したキャンドルごとに:
- SAR のドットがバーの安値の 下 で、最良入札が SAR 価格を少なくとも
MinOrderDistancePoints 上回る場合、買い指値注文は正確に SAR の値で発注(または再調整)されます。
- SAR のドットがバーの高値 * 上 * にあり、ベスト アスクが SAR 価格より少なくとも
MinOrderDistancePoints 低い場合、その SAR 価格で売り指値注文が出されます (または再調整されます)。
- 維持される未決注文は、各サイドにつき 1 つだけです。 SAR が移動すると、アクティブな未決注文がキャンセルされ、更新されたレベルで新しい注文が送信されます。
- 未決注文が約定されると、ストップロスとテイクプロフィットの距離(ポイントで表されます)は、証券価格ステップを使用して絶対価格に変換されます。これらのレベルは仮想保護境界として保存されます。
- 新しいローソク足ごとに、記録された境界がチェックされます。ローソク足の範囲がストップまたはテイクレベルに触れた場合、ストラテジーは対応するポジションを即座に閉じ、保護状態をリセットします。
パラメーター
- CandleType – シグナルキャンドルの時間枠。 MT4 入力パラメータ
timeframe に一致するように、デフォルトでは 4 時間足のローソク足になります。
- SarStep – Parabolic SAR 加速係数 (MT4 では
step)。 SAR が価格に追いつく速度を制御します。
- SarMinimum – 最大加速度 (MT4 では
maximum)。 SAR の速度を制限します。
- StopLossPoints – エントリー価格とストップレベルの間のポイント単位の距離。無効にするには、
0 に設定します。
- TakeProfitPoints – エントリー価格とテイクプロフィットレベルの間のポイント単位の距離。無効にするには、
0 に設定します。
- MinOrderDistancePoints – MT4 の
MODE_STOPLEVEL を模倣します。未決注文は、市場価格が SAR の値からこの距離よりも遠い場合にのみ送信されます。
- OrderVolume – 各未決注文のロット (ボリューム)。機器の
VolumeStep に合わせてください。
すべてのポイントベースの距離は、商品 PriceStep を使用して価格に変換されるため、その動作は市場全体で一貫したままになります。
取引行動
- 両方の方向で同時に動作します。SAR が価格を反転した場合、買い指値注文と売り指値注文が共存できます。
- 未決注文は常に最新の SAR の読み取り値と一致します。古い注文は、新しい注文が登録される前にキャンセルされます。
- 高レベルの StockSharp 戦略では、SL/TP が未決注文に直接付加されないため、ストップロスとテイクプロフィットのエグジットは、ローソク足の高値と安値を通じて仮想的に処理されます。
- この戦略は、利用可能な場合は最良の買値/売値データに依存します。それ以外の場合は、ローソク足の終値が距離条件を評価するためのフォールバックとして使用されます。
移植メモ
MinOrderDistancePoints のデフォルトは 0 ですが、取引会場が最小距離を強制している場合は、ブローカーのストップ レベルに設定できます。
- ポジションがクローズされるか未決注文がキャンセルされると、保護レベルは自動的にリセットされ、MT4 エキスパートと同じロジックが維持されます。
- C# コード内のコメントでは、メンテナンスを容易にするために、高レベルの API の使用法、インジケーター バインディング、注文のライフサイクルが説明されています。
使用のヒント
- 正確な距離を確認するためにレベル 1 の見積もりを提供します。それ以外の場合は、ローソク足の終値が現在の市場価格の適切な代用となることを確認してください。
- シンボルの
PriceStep と VolumeStep を確認して、ポイント距離と注文数量が有効な価格と数量に変換されるようにします。
- 終了は完了したローソク足で評価されるため、ストップロスの監視にさらに細かい粒度が必要な場合は、より短い時間枠を使用することを検討してください。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Parabolic SAR strategy: enters on SAR flip (price crosses SAR level).
/// Buys when price moves above SAR, sells when price drops below SAR.
/// </summary>
public class ParabolicSarLimitStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevEma;
private decimal _prevClose;
private decimal _entryPrice;
private bool _wasBullish;
private bool _hasPrev;
public ParabolicSarLimitStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 14)
.SetDisplay("EMA Length", "EMA period acting as dynamic SAR proxy.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR for volatility.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0;
_prevClose = 0;
_entryPrice = 0;
_wasBullish = false;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevEma = 0;
_prevClose = 0;
_entryPrice = 0;
_wasBullish = false;
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevEma = emaVal;
_prevClose = close;
_wasBullish = close > emaVal;
_hasPrev = true;
return;
}
var isBullish = close > emaVal;
var flip = isBullish != _wasBullish;
// Exit on SAR flip
if (Position > 0 && flip && !isBullish)
{
SellMarket();
_entryPrice = 0;
}
else if (Position < 0 && flip && isBullish)
{
BuyMarket();
_entryPrice = 0;
}
// Entry on flip
if (Position == 0 && flip)
{
if (isBullish)
{
_entryPrice = close;
BuyMarket();
}
else
{
_entryPrice = close;
SellMarket();
}
}
_prevEma = emaVal;
_prevClose = close;
_wasBullish = isBullish;
}
}
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 parabolic_sar_limit_strategy(Strategy):
def __init__(self):
super(parabolic_sar_limit_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._ema_length = self.Param("EmaLength", 14).SetDisplay("EMA Length", "EMA period acting as dynamic SAR proxy", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR for volatility", "Indicators")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(parabolic_sar_limit_strategy, self).OnReseted()
self._prev_ema = 0
self._prev_close = 0
self._entry_price = 0
self._was_bullish = False
self._has_prev = False
def OnStarted2(self, time):
super(parabolic_sar_limit_strategy, self).OnStarted2(time)
self._prev_ema = 0
self._prev_close = 0
self._entry_price = 0
self._was_bullish = False
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if not self._has_prev:
self._prev_ema = ema_val
self._prev_close = close
self._was_bullish = close > ema_val
self._has_prev = True
return
is_bullish = close > ema_val
flip = is_bullish != self._was_bullish
if self.Position > 0 and flip and not is_bullish:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0 and flip and is_bullish:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0 and flip:
if is_bullish:
self._entry_price = close
self.BuyMarket()
else:
self._entry_price = close
self.SellMarket()
self._prev_ema = ema_val
self._prev_close = close
self._was_bullish = is_bullish
def CreateClone(self):
return parabolic_sar_limit_strategy()