GitHub で見る

テンポイント3戦略

概要

  • MetaTrader 4 エキスパート アドバイザー 10p3v004 ("10points 3") を StockSharp の高レベル戦略フレームワークに変換します。
  • MACD スロープベースのグリッドエントリーロジックを、マーチンゲールスケーリング、トレーリングプロテクション、株式ベースのイグジットとともに再作成します。
  • オリジナルの EA の動作を再現したり、安全に調整したりできるように、すべてのパラメータに関する広範なドキュメントが提供されます。

取引ロジック

  1. シグナル検出。 設定されたタイムフレームの完了したローソク足ごとに、ストラテジーはユーザー定義の高速、低速、およびシグナルの長さを使用して MACD を計算します。メインの MACD 値が前のバーと比較して上昇すると、システムは長いグリッドを準備します。落ちた場合は短いグリッドが用意されます。 ReverseSignals フラグはこの解釈を反転します。
  2. グリッド エントリ。 一度にアクティブにできる方向グリッドは 1 つだけです。最初の注文はシグナルの直後に出されます。次の場合に追加注文が追加されます。
    • アクティブなグリッドの方向が現在の信号と一致し、
    • 価格は最新の約定から少なくとも GridSpacingPoints * PriceStep だけ有利な平均方向に移動しており、
    • オープングリッド取引の数は MaxTrades に達していません。 注文サイズは、小さなグリッド (最大 12 エントリ) の場合は 2^n、大きなグリッドの場合は 1.5^n を乗算し、ソース コードからマーチンゲール ロジックを再現します。最終的なサイズは機器の音量ステップに四捨五入され、セキュリティ制限と MaxVolumeCap の安全上限の両方によって制限されます。
  3. 資金管理。 UseMoneyManagement が有効な場合、基本ロット サイズは現在のポートフォリオ値と RiskPerTenThousand から導出されます。元の EA では、標準アカウントとミニアカウントに別々のルールが使用されていました。この変換では、IsStandardAccount パラメータを介して同じ動作が維持されます。設定が無効になっている場合は、固定の BaseVolume が使用されます。
  4. 終了ルール
    • オプションの 初期停止 は、集計された位置がグリッドに対して InitialStopPoints 移動した場合にグリッド全体を閉じます。
    • オプションの 利益確定 は、価格が TakeProfitPoints に達するとネット ポジションを優先してグリッドを閉じます。
    • オプションの トレーリング ストップは、平均エントリー価格から (TrailingStopPoints + GridSpacingPoints) 移動した後、価格の追従を開始し、TrailingStopPoints のトレーリング バッファーを保持します。
    • オプションの 株式保護 は、ポイントとボリュームで測定される未実現利益を監視します。 OrdersToProtect 以上のポジションがオープンし、利益が SecureProfit に達すると、戦略は直ちに終了します。

パラメーター

パラメータ 説明 デフォルト
CandleType MACD の計算と注文処理に使用される主な時間枠。 30分キャンドル
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod MACD 設定は MT4 インジケーターと同じです (デフォルトでは 14/26/9)。 14/26/9
BaseVolume グリッド位置が存在せず、資金管理が無効な場合に使用される初期ロット サイズ。 0.01
GridSpacingPoints 連続するグリッド エントリ間の最小距離。価格ステップで表されます。 15
TakeProfitPoints 完全なテイクプロフィットをトリガーするための平均エントリーからの距離。無効にするには、0 に設定します。 40
InitialStopPoints グリッドを平らにする前に許容される最大逆距離。無効にするには、0 に設定します。 0
TrailingStopPoints 後続バッファのサイズ。価格が GridSpacingPoints + TrailingStopPoints 進んだ後にトレイルが有効になります。 20
MaxTrades 方向ごとの平均オーダーの最大数。 9
OrdersToProtect 株式保護チェックが評価される前に必要なオープン取引の最小数。 3
SecureProfit 株式保護エグジットのトリガーとなる含み益目標 (ポイント × ボリューム)。 8
AccountProtectionEnabled 株式保護ブロックを有効または無効にします。 true
ReverseSignals MACD の傾きの解釈を反転します (ミラーリングされたテストに役立ちます)。 false
UseMoneyManagement RiskPerTenThousand を使用した動的体積計算を有効にします。 false
RiskPerTenThousand 資金管理がアクティブな場合に使用される残高 10,000 単位あたりのリスク量。 12
IsStandardAccount 元のロットの四捨五入ルールを複製します (true = 標準ロット、false = ミニ ロット)。 true
MaxVolumeCap マーチンゲール スケーリング後に適用されるハード キャップは、ポジション サイズを制御下に保ちます。 100

変換メモ

  • MQL の専門家は、別々のチケットレベルの停留所を維持しました。 StockSharp では、グリッドは単一の集約された位置として管理されます。したがって、トレーリングレベルとプロテクティブレベルは、出来高加重平均エントリー価格から再計算されます。
  • EA は、利益を通貨に換算するためにブローカーのティック値に依存していました。ここで、株式保護のしきい値は、ソースの pip ベースの比較を反映して、ポイントとボリュームを乗じた単位で測定されます。
  • AccountFreeMarginCheck およびその他のアカウント固有の MT4 検証には、StockSharp に直接相当するものはありません。代わりに、この戦略では、商品のボリューム境界とオプションの MaxVolumeCap が考慮されます。
  • MT4 の注文コメント、マジック ナンバー、グラフィカル アノテーションは、StockSharp に対応するものがないため再現されません。

使用法

  1. 戦略をプロジェクトに追加し、通常どおり、StockSharp 戦略に対して SecurityPortfolio を設定します。
  2. 分析する必要がある時間枠に一致するように CandleType を調整します (MT4 バージョンは現在のチャートの時間枠で機能しました)。
  3. リスク パラメータを調整します。固定の BaseVolume を維持するか、適切な RiskPerTenThousand および IsStandardAccount オプションを使用して UseMoneyManagement を有効にします。
  4. どの保護層 (イニシャルストップ、テイクプロフィット、トレーリングストップ、株式保護) を有効にするかを決定し、商品のボラティリティに合わせてしきい値を設定します。
  5. 戦略を開始します。組み込みのチャート ヘルパーは、ローソク足、MACD の値、および実行された取引を表示します。

さらなる開発のアイデア

  • 固定の GridSpacingPoints の代わりに、適応間隔ロジックを統合します (たとえば、ATR を使用)。
  • 長いグリッドと短いグリッドに対して個別の後続パラメーターを公開するか、非対称グリッドを許可します。
  • MACD の傾きとトレンド フィルター (移動平均、より高い時間枠の確認) を組み合わせて、逆トレンド グリッドの数を減らします。

注意: この戦略には、リクエストと現在のプロジェクト構造に一致する Python 実装は提供されていません。

using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// TenPoints3: RSI reversal with EMA filter and ATR stops.
/// </summary>
public class TenPoints3Strategy : 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 TenPoints3Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_emaLength = Param(nameof(EmaLength), 25)
			.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 * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
			else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevRsi = rsiVal;
	}
}