ホーム
/
戦略のサンプル
GitHub で見る
私のTS15移動平均トレーリングストップ
概要
この戦略は、既存のネット ポジション周辺のトレーリング ストップ注文を管理することにより、元の my_ts15.mq5 エキスパート アドバイザーの動作を再現します。線形加重移動平均 (LWMA) はストップの配置を制御しますが、他の平滑化手法で置き換えることもできます。 The logic continuously:
設定可能な数の完了したローソク足から移動平均値を読み取ります。
Compares price progress with the moving average trail and price-based offsets.
Moves the protective stop order only when the new level improves the previous one by at least the specified step.
オプションで、ストップをクランプするか、制限が破られたときにポジションを即座に清算することにより、最大損失距離を強制します。
この戦略はエントリーシグナルを生成しません。これは、同じ証券でポジションをオープンする他のコンポーネント (手動または自動) と一緒に実行することを目的としています。
取引ロジック
Subscribe to the selected candle series and bind a moving average indicator using the StockSharp high-level API.
ローソク足が終了したらすぐにインジケーターの結果を保存し、現在のバーから MaBarsTrail + MaShift バー後の値を取得します。
商品ティックサイズを使用して、ポイントベースの設定を絶対価格距離に変換します。
For long positions, choose the lowest of:
移動平均からそのオフセットを引いたもの。
現在の価格から「利益中」のオフセットを差し引いたもの。
Afterwards clamp the trail to the “in loss” distance and optionally to the maximum allowed loss.
ショートポジションの場合は、次の中で最も高いものを選択します。
移動平均とそのオフセット。
The current price plus the “in profit” offset.
Afterwards clamp the trail to the “in loss” distance and optionally to the maximum allowed loss.
Update the stop order only when the improvement exceeds TrailStepPoints (unless it is zero, in which case every improvement is accepted).
価格が最大損失距離に違反し、EnforceMaxStopLoss が有効になっている場合、ストラテジーはポジションを即座にクローズします。
All price inputs use the candle price specified in MaPrice, matching the original MQL setting where the indicator is fed with the PRICE_WEIGHTED series.
パラメーター
名前
デフォルト
説明
MaPeriod
50
トレーリングバックボーンとして使用される移動平均の長さ。
MaShift
0
移動平均値をサンプリングするときに適用される追加のシフト (バー単位)。
MaMethod
LinearWeighted
移動平均の平滑化方法 (単純、指数関数、平滑化、線形加重)。
MaPrice
Weighted
移動平均にフィードされたローソク足の価格。
MaBarsTrail
1
Number of completed bars between the current candle and the moving average sample.
TrailBehindMaPoints
5
Distance in points kept between the stop and the moving average.
TrailBehindPricePoints
30
ポジションが利益を生む場合に価格から維持されるポイント単位の距離。
TrailBehindNegativePoints
60
Distance in points kept behind the price when the position is losing.
TrailStepPoints
0
ストップを移動する前に必要な最小限の改善 (ポイント単位)。 Zero は「常に更新」の動作を再現します。
EnforceMaxStopLoss
false
有効にすると、ストップを最大許容損失に固定し、価格がその制限を超えたときにポジションを清算します。
MaxStopLossPoints
100
最大許容損失距離 (ポイント単位)。
ShowIndicator
true
UI が使用可能な場合は、チャート上に移動平均と取引マーカーを描画します。
CandleType
M1
計算を行うキャンドルのデータ型。
すべてのポイントベースの入力は、Security.PriceStep から計算された商品のピップサイズを介して価格距離に変換されます。
変換メモ
MQL エキスパートが MA ハンドルを手動で更新しました。 The StockSharp implementation uses BindEx to process the indicator without accessing internal buffers or calling GetValue.
買い/売り価格は完成したローソク足から直接取得できないため、末尾の計算では MaPrice によって選択されたローソク足価格が使用されます。元のスクリプトは同じ加重価格をインジケーターに供給し、それを買値/売値ティックと比較したため、これにより動作の一貫性が保たれます。
PositionModify は、保護的ストップ注文のキャンセルと再作成に置き換えられます(長い場合は SellStop、短い場合は BuyStop)。 The strategy stores the last stop level to mimic the MetaTrader trailing thresholds.
オプションの強制決済 (pre_init) は元のロジックに従います。市場が MaxStopLossPoints を超えると、ポジションは直ちに決済されます。
No entry logic has been added;ユーザーは、この後続モジュールを独自のシグナルプロバイダーと組み合わせる必要があります。
使用のヒント
Attach the strategy to the same security that opens the positions.
ポイントの距離を金融商品のティック サイズに調整します (外国為替シンボルでは通常「ピップ」値が使用されますが、CFD では異なる乗数が必要になる場合があります)。
Set TrailStepPoints to a positive value to reduce order churn on illiquid instruments.
別のリスク マネージャーがすでにハード ストップ距離を制御している場合は、EnforceMaxStopLoss を無効にします。
移動平均とトレーリングの動作を視覚化するためにパラメータを調整する間は、ShowIndicator を有効にしておきます。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// My TS15 strategy: WMA trend following with trailing stop management.
/// Enters on price crossing WMA, exits with trailing stop logic.
/// </summary>
public class MyTs15Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _trailMultiplier;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _entryPrice;
private decimal _bestPrice;
private bool _wasBullish;
private bool _hasPrevSignal;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal TrailMultiplier { get => _trailMultiplier.Value; set => _trailMultiplier.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public MyTs15Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("MA Period", "WMA period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators");
_trailMultiplier = Param(nameof(TrailMultiplier), 3m)
.SetDisplay("Trail Multiplier", "ATR multiplier for trailing stop", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 12)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_bestPrice = 0m;
_wasBullish = false;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_bestPrice = 0;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
var wma = new WeightedMovingAverage { Length = MaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(wma, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal wmaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var trailDist = atrValue * TrailMultiplier;
var isBullish = close > wmaValue;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
// Trailing stop check
if (Position > 0)
{
if (close > _bestPrice) _bestPrice = close;
if (_bestPrice - close > trailDist)
{
SellMarket();
_entryPrice = 0;
_bestPrice = 0;
_candlesSinceTrade = 0;
return;
}
}
else if (Position < 0)
{
if (close < _bestPrice) _bestPrice = close;
if (close - _bestPrice > trailDist)
{
BuyMarket();
_entryPrice = 0;
_bestPrice = 0;
_candlesSinceTrade = 0;
return;
}
}
// Entry signals
if (_hasPrevSignal && isBullish != _wasBullish && _candlesSinceTrade >= SignalCooldownCandles)
{
if (isBullish && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_bestPrice = close;
_candlesSinceTrade = 0;
}
else if (!isBullish && Position >= 0)
{
SellMarket();
_entryPrice = close;
_bestPrice = close;
_candlesSinceTrade = 0;
}
}
_wasBullish = isBullish;
_hasPrevSignal = 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 WeightedMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class my_ts15_strategy(Strategy):
def __init__(self):
super(my_ts15_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 100) \
.SetDisplay("MA Period", "WMA period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators")
self._trail_multiplier = self.Param("TrailMultiplier", 3.0) \
.SetDisplay("Trail Multiplier", "ATR multiplier for trailing stop", "Risk")
self._signal_cooldown = self.Param("SignalCooldownCandles", 12) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._wma = None
self._atr = None
self._entry_price = 0.0
self._best_price = 0.0
self._was_bullish = False
self._has_prev_signal = False
self._candles_since_trade = 0
@property
def ma_period(self):
return self._ma_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def trail_multiplier(self):
return self._trail_multiplier.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(my_ts15_strategy, self).OnReseted()
self._wma = None
self._atr = None
self._entry_price = 0.0
self._best_price = 0.0
self._was_bullish = False
self._has_prev_signal = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(my_ts15_strategy, self).OnStarted2(time)
self._wma = WeightedMovingAverage()
self._wma.Length = self.ma_period
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self._entry_price = 0.0
self._best_price = 0.0
self._has_prev_signal = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(120)))
subscription.Bind(self._wma, self._atr, self._process_candle)
subscription.Start()
def _process_candle(self, candle, wma_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._wma.IsFormed or not self._atr.IsFormed:
return
close = float(candle.ClosePrice)
wma_val = float(wma_value)
atr_val = float(atr_value)
trail_dist = atr_val * self.trail_multiplier
is_bullish = close > wma_val
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
if self.Position > 0:
if close > self._best_price:
self._best_price = close
if self._best_price - close > trail_dist:
self.SellMarket()
self._entry_price = 0.0
self._best_price = 0.0
self._candles_since_trade = 0
return
elif self.Position < 0:
if close < self._best_price:
self._best_price = close
if close - self._best_price > trail_dist:
self.BuyMarket()
self._entry_price = 0.0
self._best_price = 0.0
self._candles_since_trade = 0
return
if self._has_prev_signal and is_bullish != self._was_bullish and self._candles_since_trade >= self.signal_cooldown:
if is_bullish and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._best_price = close
self._candles_since_trade = 0
elif not is_bullish and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._best_price = close
self._candles_since_trade = 0
self._was_bullish = is_bullish
self._has_prev_signal = True
def CreateClone(self):
return my_ts15_strategy()