GitHub で見る
トレーリングストップFrCn戦略
概要
TrailingStopFrCnStrategy は、MetaTrader エキスパート アドバイザー TrailingStopFrCn.mq4 の StockSharp ポートです。元のスクリプトは、固定トレーリング距離、ビル Williams フラクタル、または最近のローソク足の高値/安値を組み合わせて、既存のポジションのストップロス レベルを管理します。このポートは、高レベルの StockSharp API と統合しながら同じ柔軟性を維持します。戦略はローソク足とレベル 1 相場をサブスクライブし、現在のネット ポジションを監視し、保護的なストップ注文を自動的に更新します。
エントリー戦略とは異なり、TrailingStopFrCn はリスク管理のみに焦点を当てています。新しいポジションはオープンしません。代わりに、Strategy.Security の既存のポジションを追跡し、ポジションが反転したときに古い逆指値注文をキャンセルし、MetaTrader アドバイザーのロジックに従う単一の集約された逆指値注文を送信します。
トレーリングロジック
- 固定トレーリング距離 –
TrailingStopPips がゼロより大きい場合、ストラテジーは元の MQL パラメータ TrailingStop のように動作します。ロング ポジションの場合、ストップは bestBid - distance に配置され、ショート ポジションの場合は bestAsk + distance に、distance = TrailingStopPips × pip size に配置されます。
- フラクタル トレーリング –
TrailingStopPips = 0 と TrailingMode = Fractals の場合、ストラテジーは 5 バーのビル Williams フラクタルを検出します。完成した各ローソク足は内部バッファーに追加され、十分な履歴が利用可能になると、2 バー前のローソク足が潜在的なフラクタルとして評価されます。現在の価格から少なくとも MinStopDistancePips 離れている最新のフラクタルが新しいストップ候補になります。
- ローソク足跡 –
TrailingStopPips = 0 および TrailingMode = Candles の場合、戦略は最後の 99 個の閉じたローソク足までをスキャンし、現在の価格から少なくとも MinStopDistancePips 離れている最初の安値 (ロングの場合) または高値 (ショートの場合) を選択します。
候補レベルを計算した後、戦略は MQL バージョンと同じ保護ルールを適用します。
- OnlyProfit は、新しいレベルで利益が確定しない限り、ストップを移動できません (ロングの場合はエントリーより上のストップ、ショートの場合はエントリーより下のストップ)。
- OnlyWithoutLoss は、アクティブなストップロスがすでにポジションを損失から保護すると、さらなるトレーリングを停止します (元のスクリプトでは、損益分岐点に達した後にトレーリングプロセスが停止します)。
- ストップは有利な方向にのみ移動します。ロングポジションの場合は上方向、ショートポジションの場合は下方向です。
StockSharp は証券ごとに単一のネット ポジションを追跡するため、逆指値注文の量は Math.Abs(Position) に等しく、すべての基礎となる約定が集計されます。
パラメーター
| パラメータ |
説明 |
OnlyProfit |
新しいレベルが平均エントリー価格と比較して利益を確保する場合にのみ、ストップロスを移動します。 MQL の OnlyProfit フラグをミラーリングします。 |
OnlyWithoutLoss |
アクティブなストップロスがエントリー価格以上になったら、トレーリングを停止します。これにより、元のアドバイザーから OnlyWithoutLoss が複製されます。 |
TrailingStopPips |
ピップ単位で表される固定トレーリング距離。フラクタルまたはローソク足跡をアクティブにするには、ゼロに設定します。 |
MinStopDistancePips |
市場価格とストップロスの間の最小距離 (ピップ単位)。これを使用して、ブローカーの MODE_STOPLEVEL 制限をエミュレートします。 |
TrailingMode |
TrailingStopPips = 0 の場合、末尾のソースを選択します。オプション: Fractals (ビル Williams の 5 バー フラクタル) または Candles (最近の安値/高値)。 |
CandleType |
フラクタルの構築またはスイング ポイントの検索に使用されるキャンドル データ タイプ。デフォルトの時間枠は 1 時間です。 |
行動メモ
- この戦略はレベル 1 データをサブスクライブして、最良の買値/売値にアクセスします。固定距離トレーリングはレベル 1 の更新に即座に反応しますが、フラクタル/キャンドル トレーリングは新しいローソクが到着すると更新されます。
- ポジションの方向が変わると、新しい注文が送信される前に現在の逆指値注文がキャンセルされます。
- 利用可能なストップ候補がない場合 (ローソクが足りないなど)、戦略は既存のストップを維持します。
- ブローカーが最小停止距離を強制しない場合は、
MinStopDistancePips をゼロのままにすることができます。
- StockSharp はネットポジションを維持しているため、個々の MetaTrader の「チケット」は追跡されません。ストップ注文は、集計されたポジション全体をカバーします。
Magic フィルタは必要ありません。戦略はすでに独自のセキュリティ コンテキストで動作しています。
- トレーリング更新は、1 秒間のポーリング ループではなく、完成したローソク足とレベル 1 データによって駆動されます。
- 元の EA のビジュアル チャート オブジェクトは再作成されません。代わりに、サンプル UI を実行するときに、StockSharp のグラフ作成ヘルパーを使用できます。
使い方のヒント
- 同じ
Security でポジションをオープンするエントリー ロジックと一緒に戦略を実行します。 TrailingStopFrCn は、ポジションが表示されると自動的にストップ注文を付加します。
- フラクタルまたはスイング ポイントを分析する必要がある時間枠に一致するように
CandleType を調整します。より高いタイムフレームではトレーリングレベルが平滑化され、より低いタイムフレームではより速く反応します。
- ブローカーのストップレベル制限に従って
MinStopDistancePips を調整します。設定が低すぎると、注文が拒否される可能性があります。
- 履歴データでテストするときは、トレーリング ロジックが正しくトリガーできるように、ローソク足サブスクリプションとレベル 1 メッセージがデータ ソースで使用できることを確認してください。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// TrailingStopFrCn: EMA crossover with ATR trailing stops.
/// </summary>
public class TrailingStopFrCnStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public TrailingStopFrCnStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 12)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 26)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class trailing_stop_fr_cn_strategy(Strategy):
def __init__(self):
super(trailing_stop_fr_cn_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 12) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 26) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(trailing_stop_fr_cn_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(trailing_stop_fr_cn_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return trailing_stop_fr_cn_strategy()