GitHub で見る

IStochastic トレーディング戦略

概要

IStochastic トレーディング戦略はMetaTrader 5エキスパートアドバイザー「IStochastic_Trading」のStockSharp直接ポートです。ボットはストキャスティクスオシレーターを使用して売られすぎと買われすぎの状態を検出し、その後ストップロス、テイクプロフィット、トレーリングストップで各エントリーを管理しながらマルチンゲール方式のポジションラダーを構築します。実装はStockSharpの高レベルAPIを通じて取得した完成ローソク足で動作し、成行注文のみに依存します。

トレードロジック

  1. 設定可能な%K長、%D平滑化、追加のスローイングファクターでストキャスティクスオシレーターを計算する。
  2. アクティブなポジションがない場合、最新の完成ローソク足を評価する:
    • %Kが%Dより上にあり%Dが設定された買いゾーンより低い場合にロングポジションを開く。
    • %Kが%Dより下にあり%Dが設定された売りゾーンより高い場合にショートポジションを開く。
  3. ポジションが存在する場合、ラダーの最新の約定を監視する:
    • 市場がトレードに対して少なくとも設定されたギャップ(pips)分逆行した場合、最大ポジション数を超えない限り同じ方向に前のボリュームの2倍で新規ポジションを開く。
  4. 各エントリーに対して銘柄のPriceStepと小数点桁数を使用してpips距離から価格ポイントに変換したトレード別のストップロスとテイクプロフィットレベルを維持する。終値がストップまたはターゲットに達した場合、戦略は成行注文で特定のポジションを終了する。
  5. 各ローソク足の終値後にトレーリングストップを適用する。トレードが有利な方向に十分動いた場合、ストップ価格は指定されたトレーリングステップ分引き締められ、ターミナルのポジション別トレーリング動作を近似する。

パラメーター

名前 デフォルト値 説明
OrderVolume 0.1 ロット単位の初期ポジションサイズ。追加エントリーは前のボリュームを2倍にする。
TakeProfitPips 50 pipsで測定したテイクプロフィット距離。値は内部で価格ポイントに変換される。
StopLossPips 50 各ポジションのpipsでのストップロス距離。
TrailingStopPips 10 pipsでのトレーリングストップ距離。トレーリングを無効にするにはゼロに設定。
TrailingStepPips 5 トレーリングストップが調整される前の最小有利な動き(pips)。
MaxPositions 3 同時に開いているマルチンゲールステップの最大数。0は制限を削除する。
GapPips 7 現在の方向に倍増する前に必要な価格ギャップ(pips)。
KPeriod 5 %K線を構築するために使用するローソク足の数。
DPeriod 3 %D平滑化平均の期間。
Slowing 3 %Kに適用される追加の平滑化。
ZoneBuy 30 ロングエントリーを検証するために使用する%Dの閾値(売られすぎゾーン)。
ZoneSell 70 ショートエントリーを検証するために使用する%Dの閾値(買われすぎゾーン)。
CandleType 15分足 計算に使用するローソク足シリーズ。

実装上の注意

  • Pips距離はPriceStepを使って価格に変換されます。3桁および5桁の相場では、MetaTraderの調整済みポイントロジックを模倣するために追加のファクター10が使用されます。
  • ストップロス、テイクプロフィット、トレーリングストップのチェックはバックテスター内でロジックを確定的に保つために閉じたローソク足価格に依存します。バー内管理が必要な場合はリアルタイム実行をカスタマイズできます。
  • 戦略は一度に一方向のラダーのみを開きます;方向を切り替える前にすべてのポジションを閉じる必要があります。
  • Python実装は要求通り意図的に省略されています。
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>
/// Stochastic Oscillator-based strategy with zone filtering.
/// Goes long when %K crosses above %D in the oversold zone, short when %K crosses below %D in overbought zone.
/// </summary>
public class IStochasticTradingStrategy : Strategy
{
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<decimal> _zoneBuy;
	private readonly StrategyParam<decimal> _zoneSell;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevK;
	private decimal? _prevD;

	/// <summary>
	/// %K period.
	/// </summary>
	public int KPeriod
	{
		get => _kPeriod.Value;
		set => _kPeriod.Value = value;
	}

	/// <summary>
	/// %D smoothing period.
	/// </summary>
	public int DPeriod
	{
		get => _dPeriod.Value;
		set => _dPeriod.Value = value;
	}

	/// <summary>
	/// Buy zone threshold (oversold).
	/// </summary>
	public decimal ZoneBuy
	{
		get => _zoneBuy.Value;
		set => _zoneBuy.Value = value;
	}

	/// <summary>
	/// Sell zone threshold (overbought).
	/// </summary>
	public decimal ZoneSell
	{
		get => _zoneSell.Value;
		set => _zoneSell.Value = value;
	}

	/// <summary>
	/// Candle type used for analysis.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public IStochasticTradingStrategy()
	{
		_kPeriod = Param(nameof(KPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("K Period", "Number of bars for %K", "Indicators");

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("D Period", "Smoothing period for %D", "Indicators");

		_zoneBuy = Param(nameof(ZoneBuy), 30m)
			.SetRange(0m, 100m)
			.SetDisplay("Buy Zone", "Upper boundary for bullish confirmation", "Signals");

		_zoneSell = Param(nameof(ZoneSell), 70m)
			.SetRange(0m, 100m)
			.SetDisplay("Sell Zone", "Lower boundary for bearish confirmation", "Signals");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe for the strategy", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevK = null;
		_prevD = null;
	}

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

		_prevK = null;
		_prevD = null;

		var stochastic = new StochasticOscillator();
		stochastic.K.Length = KPeriod;
		stochastic.D.Length = DPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(stochastic, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, stochastic);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochasticValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!stochasticValue.IsFinal)
			return;

		var stoch = (StochasticOscillatorValue)stochasticValue;

		if (stoch.K is not decimal kValue || stoch.D is not decimal dValue)
			return;

		if (_prevK is decimal prevK && _prevD is decimal prevD)
		{
			var crossedUp = prevK <= prevD && kValue > dValue;
			var crossedDown = prevK >= prevD && kValue < dValue;

			if (crossedUp && dValue < ZoneBuy && Position <= 0)
			{
				BuyMarket();
			}
			else if (crossedDown && dValue > ZoneSell && Position >= 0)
			{
				SellMarket();
			}
		}

		_prevK = kValue;
		_prevD = dValue;
	}
}