GitHub で見る
トレーリングストップ戦略のサンプル
概要
SampleTrailingStopStrategy は、MetaTrader エキスパート アドバイザー SampleTrailingstop.mq4 の直接 C# ポートです。ストラテジーは独自のエントリを生成しません。代わりに、現在のポジションを継続的に監視し、保護的なストップロス注文と利益確定注文を維持します。このロジックは、価格ポイントで測定されるトレーリング ストップを適用しながら、ブローカーが課したストップ レベルとフリーズ レベルを尊重することで、元の EA を反映しています。
ロングポジションが利益を得ることができ、最高入札額がエントリー価格から十分に離れた場合、この戦略はまずストップロスを入札額のすぐ下に最小許容距離だけ移動させます。後続の更新は、設定されたポイント数にブローカー バッファーを加えた分だけ、入札の後ろのストップを追跡します。ショートポジションは対称的に処理され、ストップはアスクの上にあります。オプションのテイクプロフィット目標は、後続のイベントごとに再計算されます。
データフロー
- レベル 1 の更新を購読して、最高の買値/売値を受け取ります。
- ベース
Strategy API を通じて現在のポジション価格を追跡します。
- 新しい価格が計算されるときに、保護的なストップ注文と指値注文を再登録します。
パラメーター
| パラメータ |
デフォルト |
説明 |
TrailingStopPoints |
200 |
価格ポイントで測定された市場とトレーリングストップの間の距離。この値は、後続の計算中にブローカーのバッファーに追加されます。 |
TakeProfitPoints |
1000 |
ポイント単位のオプションのテイクプロフィットディスタンス。テイクプロフィット管理を無効にするには、0 に設定します。 |
StopLevelPoints |
0 |
ブローカーのストップレベル制限をポイントで表します。ストップ注文を有効に保つために、トレーリングディスタンスに追加されます。 |
FreezeLevelPoints |
0 |
ブローカーのフリーズレベル制限をポイントで表します。トレーリングは、市場がエントリー価格からこのバッファーを超えるまで待機します。 |
すべての距離は、MetaTrader からの _Point の動作をエミュレートするために、商品ティック サイズを使用して価格値に変換されます。
トレーリングアルゴリズム
- ポジションの検証 – この戦略は、ポジションが存在し、最良の買値/売値が判明するまで、トレーリングを無視します。
- 利益チェック – トレーリングは、ポジションに利益があり(ロングの場合は
bid > entry、ショートの場合は ask < entry)、フリーズ バッファがクリアされている場合にのみ有効になります。
- 最初のストップの配置 – まだアクティブなトレーリング ストップがない場合は、価格がエントリーから少なくともトレーリング距離離れた時点で、ストップは市場から許容される最小距離 (ロングの場合は買い値マイナス バッファー、ショートの場合はアスク プラス バッファー) に移動されます。
- トレーリングの更新 – ポジションが収益性を維持している間、設定されたトレーリング距離とブローカーのバッファーを使用してストップがさらに深くなります。有効にすると、更新ごとにテイクプロフィットレベルが再計算されます。
- 注文メンテナンス – プロテクション注文は高レベルのヘルパー メソッドを通じて自動的に作成、更新、キャンセルされるため、ブローカーは常に最新のストップロスとテイクプロフィットの値を確認できます。
使用上の注意
- ポジションをオープンする別のコンポーネントと一緒に戦略を開始するか、手動注文を使用します。このモジュールは出口のみを管理します。
- 商品のメタデータに適切な価格と出来高のステップが含まれていることを確認してください。この戦略は、為替の制約を満たすために、生成されたすべての価格と金額を正規化します。
- ポジションの方向が反転すると、新しい側のトレーリングリスタートの前に、従来の保護命令はすべてキャンセルされます。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Sample Trailing Stop strategy: Smoothed MA crossover.
/// Buys when close crosses above Smoothed MA, sells when crosses below.
/// </summary>
public class SampleTrailingStopStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal _prevClose;
private decimal _prevSma;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public SampleTrailingStopStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 50)
.SetGreaterThanZero()
.SetDisplay("Period", "Smoothed MA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
var smma = new ExponentialMovingAverage { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(smma, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smmaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevClose <= _prevSma && candle.ClosePrice > smmaValue && Position <= 0)
BuyMarket();
else if (_prevClose >= _prevSma && candle.ClosePrice < smmaValue && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevSma = smmaValue;
_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 sample_trailing_stop_strategy(Strategy):
def __init__(self):
super(sample_trailing_stop_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 50)
self._prev_close = 0.0
self._prev_sma = 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 Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
def OnReseted(self):
super(sample_trailing_stop_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(sample_trailing_stop_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
smma = ExponentialMovingAverage()
smma.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(smma, self._process_candle).Start()
def _process_candle(self, candle, smma_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
smma_val = float(smma_value)
if self._has_prev:
if self._prev_close <= self._prev_sma and close > smma_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_close >= self._prev_sma and close < smma_val and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_sma = smma_val
self._has_prev = True
def CreateClone(self):
return sample_trailing_stop_strategy()