GitHub で見る
SilverTrend Duplex戦略
概要
SilverTrend Duplex戦略はMetaTrader 5エキスパートアドバイザーExp_SilverTrend_DuplexのStockSharpポートです。元のロボットは2つの独立したSilverTrendフィルター(ロングとショートの決定用)を組み合わせ、インジケーターの色が強気状態と弱気状態の間で切り替わるときにトレードを実行します。このC#実装はデュアルフィルターアーキテクチャを維持し、StockSharpの高レベルAPIを活用しながらロングとショートのロジックを個別に調整できます。
戦略は完了したロウソク足のみで動作します。2つの別々のサブスクリプションを設定できるため、ロングとショートのシグナルは必要に応じて異なる時間軸や銘柄を観察できます。内部では、カスタムSilverTrendIndicatorがDonchianチャネルの極値とリスク乗数を組み合わせて元のSilverTrendバンドをエミュレートすることでMQLバージョンのカラーロジックを再構築します。
取引ロジック
インジケーターの再構築
- 各ロウソク足に対して
SSPバー上のDonchianの上限と下限が計算されます。
- 適応閾値
sminとsmaxはリスク係数(33 - risk)を使用して導出され、MLQアルゴリズムと同一です。
- 価格が
smaxより上で閉じると強気状態が記録され、sminより下で閉じると弱気状態が記録されます;それ以外の場合は前の状態が保持されます。ロウソク足のボディ方向が元のSilverTrendインジケーターと全く同様に最終的なカラーコード(0..4)を決定します。
シグナルの準備
- カラー値はロングフィルターとショートフィルターの両方について、最新の
SignalBar + 1個の完了したロウソク足に対して保存されます。
- ロングシグナルは選択されたオフセットでの色が
2未満(強気)に落ちながら前の色が1より大きい(非強気)場合にトリガーされ、MQLのValue[1] < 2 && Value[0] > 1を再現します。
- ショートシグナルは色が
2より上(弱気)に上昇し、前の色が0より上の場合にトリガーされ、スクリプトのValue[1] > 2 && Value[0] > 0に一致します。
注文実行
- エントリーは
Volume + |Position|に等しいボリュームでBuyMarketまたはSellMarketを使用し、反対側のエクスポージャーを決済して新しい側を1つのマーケット注文で開きます。
- 出口はインジケーターが反対のカラーバンドに戻ることに依存します。ロングポジションは色が
2より上に動くと決済され、ショートポジションは2未満に落ちると決済されます。
戦略はTradeAlgorithms.mqhの元のマネーマネジメントマトリクスやサーバーサイドのストップ配置を再現しません。したがってリスク制御はStockSharpの組み込み保護メカニズムまたはブローカールールで管理する必要があります。
パラメーター
| 名前 |
デフォルト |
説明 |
LongCandleType |
4時間ロウソク足 |
ロングサイドインジケーターに使用するデータタイプ。 |
LongSsp |
9 |
ロングフィルターのSilverTrendルックバック長。 |
LongRisk |
3 |
チャネル幅に適用するリスク乗数(33 - risk)。 |
LongSignalBar |
1 |
ロングシグナル評価のオフセット(完了したロウソク足単位)。≥ 1でなければなりません。 |
EnableLongEntries |
true |
ロングポジションのオープンを切り替えます。 |
EnableLongExits |
true |
弱気色が現れた際のロングポジションのクローズを切り替えます。 |
ShortCandleType |
4時間ロウソク足 |
ショートサイドインジケーターに使用するデータタイプ。 |
ShortSsp |
9 |
ショートフィルターのSilverTrendルックバック長。 |
ShortRisk |
3 |
ショートフィルターのリスク乗数。 |
ShortSignalBar |
1 |
ショートシグナル評価のオフセット。≥ 1でなければなりません。 |
EnableShortEntries |
true |
ショートポジションのオープンを切り替えます。 |
EnableShortExits |
true |
強気色が現れた際のショートポジションのクローズを切り替えます。 |
Volume |
1 |
エントリーに使用するベース注文ボリューム。 |
実装メモ
- シグナルはインジケーターとカラー履歴の両方が十分なデータ(
SignalBar + 1の値)を含んでいる場合にのみ評価されます。これはMQLエキスパートのBarsCalculatedチェックを反映しています。
- カスタムインジケーターは生のバッファーデータをコピーする代わりに10進数のカラー値を公開します。
Bind高レベルAPIのおかげでGetValueへの直接呼び出しは必要ありません。
- ロングとショートのロウソク足タイプが同一の場合、パラメーターセットを分離するために意図的に2つのサブスクリプションが作成されます。これは元のアドバイザーのダブルハンドル動作に一致します。
- ソーススクリプトのストップロス、テイクプロフィット、スリッページ、マージン管理オプションは再現されません。同様の動作が必要な場合はStockSharpリスクルール(例:
StopLossRule)を追加できます。
使用のヒント
- 各市場レジームにブレイクアウト閾値を適応させるために
LongSsp、ShortSspおよび対応するリスク値を別々に最適化します。
- 元の「前のバーのシグナル」動作をエミュレートしたい場合は
SignalBarを1に維持します。より大きな値は反応する前に戦略に追加バーを待つことを強制します。
- SilverTrendの色の切り替えは横ばい市場で頻繁なレジーム変化を引き起こす可能性があるため、複数の銘柄で実行する場合は戦略をポートフォリオレベルのリスク制御や時間フィルターと組み合わせます。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SilverTrend Duplex strategy (simplified). Uses ATR channel breakout.
/// </summary>
public class SilverTrendDuplexStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _emaLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public SilverTrendDuplexStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_atrLength = Param(nameof(AtrLength), 10)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Indicators");
_emaLength = Param(nameof(EmaLength), 21)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA trend", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, (ICandleMessage candle, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0m)
return;
if (close > emaValue + range * 1.5m && Position <= 0)
BuyMarket();
else if (close < emaValue - range * 1.5m && Position >= 0)
SellMarket();
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 silver_trend_duplex_strategy(Strategy):
def __init__(self):
super(silver_trend_duplex_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._atr_length = self.Param("AtrLength", 10) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._ema_length = self.Param("EmaLength", 21) \
.SetDisplay("EMA Length", "EMA trend", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
def OnStarted2(self, time):
super(silver_trend_duplex_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ev = float(ema_value)
rng = float(candle.HighPrice) - float(candle.LowPrice)
if rng <= 0:
return
if close > ev + rng * 1.5 and self.Position <= 0:
self.BuyMarket()
elif close < ev - rng * 1.5 and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return silver_trend_duplex_strategy()