GitHub で見る

Starter V6 Mod戦略(StockSharp変換版)

概要

Starter V6 Mod戦略は、MetaTrader 5のエキスパートアドバイザーStarter_v6modをStockSharpの高レベルAPIに変換したものです。元のシステムはLaguerre RSIオシレーター、二重指数移動平均、コモディティチャンネルインデックスフィルター、グリッドスタイルのポジション管理モジュールを組み合わせています。このポートは多層確認ロジックを保持しながら、ポジション管理、資金管理、保護アクションをStockSharp環境に適応させています。

トレードロジック

インジケーター

  • Laguerre RSIプロキシ – 元のLaguerreオシレーターが使用する0-1スケールをエミュレートするために、正規化された14期間RSIでモデル化されています。レベルペアLevelDown / LevelUp(デフォルト0.15 / 0.85)が売られすぎと買われすぎゾーンを定義します。
  • スローEMA (120)ファストEMA (40) – 両方ともメジアン足価格で計算されます。その相対的な変位がトレンド方向フィルターとして機能します。AngleThresholdパラメーターはEMAスプレッドをティック距離に変換し、トレード方向を制御します。
  • コモディティチャンネルインデックス (14) – ロングエントリーには負の値、ショートエントリーには正の値を要求することでモメンタム方向を確認します。

エントリー条件

  1. EMAスプレッドからトレンドバイアスを決定:
    • スローEMAマイナスファストEMAが-AngleThresholdティック未満の場合、ロングポジションのみ開始可能。
    • スプレッドがAngleThresholdより大きい場合、ショートのみ開始可能。
    • それ以外の場合、市場はフラットとみなされ新しいポジションは開かれません。
  2. トレンドバイアスが方向を許可する場合、オシレーターとモメンタムフィルターを確認:
    • ロング設定 – LaguerreプロキシがLevelDown以下、スローEMA < 前のスローEMA、ファストEMA < 前のファストEMA、かつCCI < 0。
    • ショート設定 – LaguerreプロキシがLevelUp以上、スローEMA > 前のスローEMA、ファストEMA > 前のファストEMA、かつCCI > 0。
  3. グリッド間隔 – 同じ方向にポジションを積み重ねる場合、現在価格は最低ロングエントリーより少なくともGridStepPips下、または最高ショートエントリーより上でなければなりません。これは元のEAのアベレージングロジックを再現します。
  4. ポジション数 – 同時グリッドエントリーの総数はMaxOpenTradesを超えられません。

エグジット条件

  • Laguerreエグジット – オシレーターがLevelUpを上抜けるとロングがクローズ、LevelDownを下抜けるとショートがクローズ。
  • ストップロス / テイクプロフィット – ピップスで表現され、インストゥルメントの価格増分に変換されます。変換は3/5桁価格のシンボルに対する元の調整を追跡します。
  • トレーリングストップ – 価格が(TrailingStopPips + TrailingStepPips)前進した後にアクティベートされ、TrailingStopPipsのオフセットで価格に追従します。
  • 金曜日の保護 – 18:00(ターミナル時刻)以降は新規取引を許可せず、20:00以降にすべての未決済ポジションを清算します。

資金管理

  • ボリュームサイジング – 固定(UseManualVolume = true)またはリスクベース。リスクモードでは、ボリュームは(資産 * RiskPercent) / (価格単位でのストップロス距離)に等しくなります。
  • 資産カットオフ – 現在の資産がEquityCutoffを下回るとトレードが停止します。
  • 日次損失制限 – 戦略が当日MaxLossesPerDay回の損失エグジットを記録すると、以降のポジションは開かれません。
  • 損失回復 – 各損失エグジット後、次のポジションサイズはDecreaseFactor^今日の損失数で割られ、元のポジションスケーリングロジックを反映します。

実装メモ

  • この変換はStockSharpの高レベルSubscribeCandles().Bind(...)パイプラインを使用して、完成した足とインジケーター値を決定ロジックにストリーミングします。
  • StockSharpにはネイティブのLaguerre RSIが含まれていないため、正規化されたRSIがプロキシとして使用されます。閾値はLaguerreの0-1範囲に一致します。
  • EMAアングルフィルターは、スローとファストEMA値の差をティックで測定することで再現され、元のemaangleカスタムインジケーターに似た方向ゲートを提供します。
  • 手動ストップとトレーリング管理は、MQLのトレーリング修正との同等性を維持するために足処理ルーティン内で実行されます。
  • グリッドブックキーピングは、StockSharpの集約ポジションモデル内で動作しながらMQLのマルチポジションワークフローをエミュレートするために、平均エントリー価格、最低/最高約定価格、トレーリングレベルを追跡します。

パラメーター

名前 デフォルト値 説明
UseManualVolume false 固定とリスクベースのポジションサイジングを切り替えます。
ManualVolume 1 手動サイジングが有効な場合、またはリスクサイジングを計算できない場合に使用するボリューム。
RiskPercent 5 自動サイジングがアクティブな場合のトレードあたりのリスク資産割合。
StopLossPips 35 ストップロス距離(ピップス)。
TakeProfitPips 10 テイクプロフィット距離(ピップス)。
TrailingStopPips 0 トレーリングストップ距離(ピップス)(0でトレーリングを無効化)。
TrailingStepPips 5 トレーリングストップが価格追従を開始する前の最小前進量。
DecreaseFactor 1.6 各損失後にサイズを縮小するために適用される係数。
MaxLossesPerDay 3 暦日あたりに許可される損失エグジットの最大数。
EquityCutoff 800 新規取引を停止する資産閾値。
MaxOpenTrades 10 同時グリッドエントリーの最大数。
GridStepPips 30 同方向での積み重ねエントリー間の最小間隔。
LongEmaPeriod 120 スローEMAフィルターの期間。
ShortEmaPeriod 40 ファストEMAフィルターの期間。
CciPeriod 14 コモディティチャンネルインデックスの期間。
AngleThreshold 3 ティックで表現されたEMAスプレッド閾値。
LevelUp 0.85 上部Laguerreレベル。
LevelDown 0.15 下部Laguerreレベル。
CandleType 15m 計算に使用する足の時間軸。

使用のヒント

  1. CandleTypeパラメーターを元のMT5設定で使用される時間軸に合わせて設定してください(このEAは15分足チャートに展開されることが多いです)。
  2. リスク設定を口座仕様に合わせてください。リスクベースのサイジングを使用する場合、StopLossPipsがインストゥルメントのボラティリティを反映していることを確認してください。計算されるボリュームに直接影響します。
  3. 取引所の取引時間を確認してください。組み込みの金曜日保護は、サーバークロックが希望するセッション終了と一致していることを前提としています。
  4. デバッグや最適化のために、EMA、RSIプロキシ、CCI、実行されたトレードを視覚化するためにチャート描画(CreateChartArea経由)を有効にしてください。
  5. MT5バックテストからパラメーターセットを移植する場合、RSIプロキシはLaguerreオシレーターを近似するものであることを念頭に置いてください。元のシグナルタイミングに合わせるために微細な閾値調整が必要な場合があります。

ファイル

  • CS/StarterV6ModStrategy.cs – StockSharp戦略実装。
  • README.md – 英語ドキュメント(このファイル)。
  • README_zh.md – 簡体字中国語ドキュメント。
  • README_ru.md – ロシア語ドキュメント。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Conversion of the Starter_v6mod Expert Advisor using the high-level StockSharp API.
/// </summary>
public class StarterV6ModStrategy : Strategy
{
	private readonly StrategyParam<bool> _useManualVolume;
	private readonly StrategyParam<decimal> _manualVolume;
	private readonly StrategyParam<decimal> _riskPercent;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<int> _trailingStopPips;
	private readonly StrategyParam<int> _trailingStepPips;
	private readonly StrategyParam<decimal> _decreaseFactor;
	private readonly StrategyParam<int> _maxLossesPerDay;
	private readonly StrategyParam<decimal> _equityCutoff;
	private readonly StrategyParam<int> _maxOpenTrades;
	private readonly StrategyParam<int> _gridStepPips;
	private readonly StrategyParam<int> _longEmaPeriod;
	private readonly StrategyParam<int> _shortEmaPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _angleThreshold;
	private readonly StrategyParam<decimal> _levelUp;
	private readonly StrategyParam<decimal> _levelDown;
	private readonly StrategyParam<DataType> _candleType;

	private ExponentialMovingAverage _longEma;
	private ExponentialMovingAverage _shortEma;
	private CommodityChannelIndex _cci;
	private RelativeStrengthIndex _laguerreProxy;

	private decimal? _prevLongEma;
	private decimal? _prevShortEma;

	/// <summary>
	/// Use manual volume instead of risk calculation.
	/// </summary>
	public bool UseManualVolume
	{
		get => _useManualVolume.Value;
		set => _useManualVolume.Value = value;
	}

	/// <summary>
	/// Manual volume for each new entry.
	/// </summary>
	public decimal ManualVolume
	{
		get => _manualVolume.Value;
		set => _manualVolume.Value = value;
	}

	/// <summary>
	/// Risk percentage used when position sizing is automatic.
	/// </summary>
	public decimal RiskPercent
	{
		get => _riskPercent.Value;
		set => _riskPercent.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in pips.
	/// </summary>
	public int StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in pips.
	/// </summary>
	public int TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in pips.
	/// </summary>
	public int TrailingStopPips
	{
		get => _trailingStopPips.Value;
		set => _trailingStopPips.Value = value;
	}

	/// <summary>
	/// Additional distance required before the trailing stop starts to follow the price.
	/// </summary>
	public int TrailingStepPips
	{
		get => _trailingStepPips.Value;
		set => _trailingStepPips.Value = value;
	}

	/// <summary>
	/// Multiplier used to reduce the position size after losses.
	/// </summary>
	public decimal DecreaseFactor
	{
		get => _decreaseFactor.Value;
		set => _decreaseFactor.Value = value;
	}

	/// <summary>
	/// Maximum number of losing trades allowed per day.
	/// </summary>
	public int MaxLossesPerDay
	{
		get => _maxLossesPerDay.Value;
		set => _maxLossesPerDay.Value = value;
	}

	/// <summary>
	/// Equity threshold below which the strategy stops opening new trades.
	/// </summary>
	public decimal EquityCutoff
	{
		get => _equityCutoff.Value;
		set => _equityCutoff.Value = value;
	}

	/// <summary>
	/// Maximum number of simultaneously opened grid positions.
	/// </summary>
	public int MaxOpenTrades
	{
		get => _maxOpenTrades.Value;
		set => _maxOpenTrades.Value = value;
	}

	/// <summary>
	/// Grid step in pips used when stacking positions.
	/// </summary>
	public int GridStepPips
	{
		get => _gridStepPips.Value;
		set => _gridStepPips.Value = value;
	}

	/// <summary>
	/// Period for the slow EMA trend filter.
	/// </summary>
	public int LongEmaPeriod
	{
		get => _longEmaPeriod.Value;
		set => _longEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for the fast EMA trend filter.
	/// </summary>
	public int ShortEmaPeriod
	{
		get => _shortEmaPeriod.Value;
		set => _shortEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for the CCI momentum filter.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Threshold in ticks for the EMA spread trend detector.
	/// </summary>
	public decimal AngleThreshold
	{
		get => _angleThreshold.Value;
		set => _angleThreshold.Value = value;
	}

	/// <summary>
	/// Upper Laguerre RSI level.
	/// </summary>
	public decimal LevelUp
	{
		get => _levelUp.Value;
		set => _levelUp.Value = value;
	}

	/// <summary>
	/// Lower Laguerre RSI level.
	/// </summary>
	public decimal LevelDown
	{
		get => _levelDown.Value;
		set => _levelDown.Value = value;
	}

	/// <summary>
	/// Candle data type used by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="StarterV6ModStrategy"/> class.
	/// </summary>
	public StarterV6ModStrategy()
	{
		_useManualVolume = Param(nameof(UseManualVolume), true)
		.SetDisplay("Manual Volume", "Use manual volume instead of risk-based sizing", "Money Management");

		_manualVolume = Param(nameof(ManualVolume), 1m)
		.SetRange(0.01m, 100m)
		.SetDisplay("Volume", "Manual volume per trade", "Money Management")
		;

		_riskPercent = Param(nameof(RiskPercent), 5m)
		.SetRange(0.5m, 20m)
		.SetDisplay("Risk %", "Risk percentage when auto-sizing trades", "Money Management")
		;

		_stopLossPips = Param(nameof(StopLossPips), 35)
		.SetRange(0, 500)
		.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk Management");

		_takeProfitPips = Param(nameof(TakeProfitPips), 10)
		.SetRange(0, 500)
		.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk Management");

		_trailingStopPips = Param(nameof(TrailingStopPips), 0)
		.SetRange(0, 500)
		.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk Management");

		_trailingStepPips = Param(nameof(TrailingStepPips), 5)
		.SetRange(0, 500)
		.SetDisplay("Trailing Step", "Additional distance before trailing activates", "Risk Management");

		_decreaseFactor = Param(nameof(DecreaseFactor), 1.6m)
		.SetRange(1m, 10m)
		.SetDisplay("Decrease Factor", "Volume reduction factor after losses", "Money Management");

		_maxLossesPerDay = Param(nameof(MaxLossesPerDay), 3)
		.SetRange(0, 20)
		.SetDisplay("Daily Loss Limit", "Maximum number of losses per day", "Risk Management");

		_equityCutoff = Param(nameof(EquityCutoff), 800m)
		.SetRange(0m, 1_000_000m)
		.SetDisplay("Equity Cutoff", "Stop trading if equity drops below this value", "Risk Management");

		_maxOpenTrades = Param(nameof(MaxOpenTrades), 10)
		.SetRange(1, 100)
		.SetDisplay("Max Trades", "Maximum simultaneous grid positions", "General");

		_gridStepPips = Param(nameof(GridStepPips), 30)
		.SetRange(0, 500)
		.SetDisplay("Grid Step", "Minimum pip distance between stacked entries", "General");

		_longEmaPeriod = Param(nameof(LongEmaPeriod), 120)
		.SetRange(10, 400)
		.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
		;

		_shortEmaPeriod = Param(nameof(ShortEmaPeriod), 40)
		.SetRange(5, 200)
		.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
		;

		_cciPeriod = Param(nameof(CciPeriod), 14)
		.SetRange(5, 100)
		.SetDisplay("CCI Period", "CCI indicator length", "Indicators")
		;

		_angleThreshold = Param(nameof(AngleThreshold), 3m)
		.SetRange(0m, 50m)
		.SetDisplay("Angle Threshold", "EMA spread threshold measured in ticks", "Indicators");

		_levelUp = Param(nameof(LevelUp), 0.85m)
		.SetRange(0.1m, 1m)
		.SetDisplay("Laguerre Up", "Upper Laguerre RSI level", "Indicators");

		_levelDown = Param(nameof(LevelDown), 0.15m)
		.SetRange(0m, 0.9m)
		.SetDisplay("Laguerre Down", "Lower Laguerre RSI level", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
		.SetDisplay("Candle Type", "Timeframe used for analysis", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_longEma = null;
		_shortEma = null;
		_cci = null;
		_laguerreProxy = null;
		_prevLongEma = null;
		_prevShortEma = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_longEma = new ExponentialMovingAverage { Length = LongEmaPeriod };
		_shortEma = new ExponentialMovingAverage { Length = ShortEmaPeriod };
		_cci = new CommodityChannelIndex { Length = CciPeriod };
		_laguerreProxy = new RelativeStrengthIndex { Length = 14 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_longEma, _shortEma, _cci, _laguerreProxy, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent));

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _longEma);
			DrawIndicator(area, _shortEma);
			DrawIndicator(area, _cci);
			DrawIndicator(area, _laguerreProxy);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal longEmaValue, decimal shortEmaValue, decimal cciValue, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevLongEma is null || _prevShortEma is null)
		{
			_prevLongEma = longEmaValue;
			_prevShortEma = shortEmaValue;
			return;
		}

		if (Position != 0)
		{
			_prevLongEma = longEmaValue;
			_prevShortEma = shortEmaValue;
			return;
		}

		var laguerre = rsiValue / 100m;

		// Buy: RSI low (oversold), EMAs falling (pullback), CCI negative
		var buySignal = laguerre < LevelDown && cciValue < 0m;

		// Sell: RSI high (overbought), EMAs rising, CCI positive
		var sellSignal = laguerre > LevelUp && cciValue > 0m;

		if (buySignal)
			BuyMarket();
		else if (sellSignal)
			SellMarket();

		_prevLongEma = longEmaValue;
		_prevShortEma = shortEmaValue;
	}
}