GitHub で見る
4119 チャンピオン戦略
概要
この戦略は、MQL/919/champion.mq5 にある MetaTrader Expert Advisor の高レベル C# ポートです。元の EA は、相対強度指数 (RSI) シグナルを待ち、予想されるブレイクアウトの方向に 3 つのストップ注文を出します。すべての未決注文にはすでにストップロスとテイクプロフィットが含まれており、価格が有利に推移するたびにストップロスが追跡されます。 StockSharp バージョンは、高レベルの API 呼び出し (SubscribeCandles、Bind、BuyStop、SellStop など) のみに依存しながら、同じ動作を維持します。
デフォルト設定は、MetaTrader の「ポイント」が StockSharp PriceStep (通常は 0.0001) と一致するリキッド FX 金融商品をターゲットとしています。ローソク足のタイプは設定可能であり、アカウントが最良の買い/売り気配と、オプションでストップレベル情報を提供している限り、戦略は任意の時間枠に適用できます。
戦略ロジック
- 信号生成
- 設定可能な長さの RSI は、完成したキャンドルに基づいて計算されます。
- 以前の RSI 値 (1 つ前の閉じたバー) が対称しきい値 (
RsiLevel) と比較されます。
RSI < RsiLevel は強気のセットアップをトリガーします。 RSI > 100 - RsiLevel は弱気セットアップをトリガーします。
- 注文保留中
- オープンポジションがなく、ストラテジーによって管理されるアクティブな未決注文もない場合、シグナルされた方向に 3 つの同一のストップ注文が出されます。
- 買いストップは最良売り値より上に配置され、売りストップは最良買い値より下に配置されます。この距離は、サーバーが提供する停止レベル (利用可能な場合) または
MinOrderDistancePoints フォールバックを考慮します。
- 注文量は動的に計算されます。利用可能なアカウントの値を
BalancePerLot で割って、[0.1, 15] のロット範囲に固定され、小数点第 2 位に四捨五入されます。各未決注文は、計算された量の 3 分の 1 を受け取ります。
- 最初の保護命令
- 最初の取引が成立するとすぐに、集約された保護注文が登録されます。ストップロスは
entry ± StopLossPoints、テイクプロフィットは entry ± TakeProfitPoints です (MetaTrader ポイントは PriceStep までに価格に変換されます)。
TakeProfitPoints がゼロの場合、利食い注文は無効になります。
- トレーリングストップ
- ポジションがオープンの間、ストップロス注文はレベル 1 更新ごとに強化されます。
- 長い間、新しいストップは
max(entry + spread, bid - StopLoss) に等しくなります。ショーツ用min(entry - spread, ask + StopLoss)。
- トレーリングは、動きがブローカーのストップレベルと現在のスプレッドの合計を超えた場合にのみアクティブになり、元の EA の安全策が再現されます。
- 保留中の注文メンテナンス
- 保留中の買いストップは、アクティベーション価格が現在の売り値から
RepriceDistancePoints 以上離れている場合、市場に近づけられます。同じロジックが現在の入札に対する売りストップにも適用されます。
- 再価格設定は常に、
RepriceDistancePoints と有効停止レベル距離の大きい方を優先します。
- ポジションエグジット
- ポジションは、保護的なストップロス/テイクプロフィット注文、またはユーザーの手動介入によってクローズされます。ポジションサイズがゼロに戻ると、戦略は残りの保護注文をキャンセルし、次の RSI シグナルを待ちます。
パラメーター
| パラメータ |
説明 |
TakeProfitPoints |
テイクプロフィット注文を出すために、約定価格に MetaTrader ポイントが追加または差し引かれます。ターゲットを無効にするには、0 に設定します。 |
StopLossPoints |
ストップロス注文を出し、トレーリングディスタンスを計算するために、約定価格に MetaTrader ポイントが加算または減算されます。 |
RsiPeriod |
RSI の長さ (キャンドルの数)。 |
RsiLevel |
対称の RSI しきい値。このレベルを下回る値はロングをトリガーし、100 - level を上回る値はショートをトリガーします。 |
BalancePerLot |
ポジションをサイジングする際に、1 つの標準ロットに相当するとみなされる口座通貨金額。 |
MinOrderDistancePoints |
取引会場がストップレベルを報告しない場合の、市場価格と新しいストップ注文の間のフォールバック最小距離 (ポイント単位)。 |
RepriceDistancePoints |
未決注文の再価格設定をトリガーする距離 (ポイント単位)。 |
CandleType |
RSI の計算に使用されるローソク足のデータ型。 |
使用上の注意
- この戦略には、ローソク足データとレベル 1 相場 (最良の買い値/売り値) の両方が必要です。レベル 1 の更新がない場合、トレーリング ロジックと保留中のメンテナンスは無効になります。
- ブローカーがレベル 1 メタデータを通じてストップレベルまたはストップディスタンスを公開すると、それが自動的に受け入れられます。それ以外の場合は、機器の要件に一致するように
MinOrderDistancePoints を構成します。
- ポートフォリオ情報が欠落しているか、計算されたロットサイズが正でなくなった場合、ポジションサイジングは
Strategy.Volume プロパティに戻ります。
- 3 つの未決注文は常に一緒に発注されます。部分的な参加が必要な場合は、不要な注文を手動でキャンセルします。戦略は残りの管理を継続します。
リスク管理
- ストップロス注文とテイクプロフィット注文はネイティブの取引所/ブローカー注文であり、MetaTrader EA の動作を反映しています。ポジションが決済されると、保護命令は直ちにキャンセルされます。
- トレーリングストップは利益の方向にのみ動き、ストップロスを緩めることはありません。価格がエントリー価格を少なくとも
(StopLevel + spread) 超えた場合に有効になります。
- 価格再設定ロジックにより、大幅なジャンプ後に古い未決注文が取り残されることがなくなり、約定が遅れる可能性が低くなります。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Champion: RSI breakout with EMA trend filter and ATR trailing.
/// </summary>
public class ChampionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public ChampionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevRsi == 0 || atrVal <= 0)
{
_prevRsi = rsiVal;
return;
}
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || rsiVal > 70)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || rsiVal < 30)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (_prevRsi <= 30 && rsiVal > 30 && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (_prevRsi >= 70 && rsiVal < 70 && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevRsi = rsiVal;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class champion_strategy(Strategy):
def __init__(self):
super(champion_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(champion_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 2.0 or rv > 70:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 2.0 or rv < 30:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self.Position == 0:
if self._prev_rsi <= 30 and rv > 30 and close > ev:
self._entry_price = close
self.BuyMarket()
elif self._prev_rsi >= 70 and rv < 70 and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(champion_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
def CreateClone(self):
return champion_strategy()