GitHub で見る

CHO 平滑化された EA 戦略

概要

この戦略は、元の「CHO Smoothed EA」エキスパートアドバイザーのロジックを複製します。完了したローソク足のチャイキン オシレーターのクロスオーバーを監視し、構成可能な移動平均でオシレーターを平滑化します。オプションのフィルターは、取引を特定のセッションに制限し、取引の方向を制限し、ゼロライン確認でシグナルを検証します。シグナルが受け入れられると、戦略は成行注文を送信し、ストップロス、テイクプロフィット、トレーリングプロテクションのためのポイント単位の固定距離を使用してポジションを管理します。

取引ロジック

  • チャイキン オシレーターの値は、設定可能な速い周期と遅い周期を使用して、完成したキャンドルごとに計算されます。
  • オシレーターの移動平均により信号線が作成されます。移動平均の期間と種類を調整できます。
  • ロングエントリーは、オシレーターが平滑化されたラインを上回ったときに発生します。ショートエントリーは反対側のクロスオーバーで発生します。シグナルを反転して、元の方向と逆にトレードすることができます。
  • ゼロレベルフィルターが有効な場合、両方のオシレーター値はロングトレードの場合はゼロ未満、ショートトレードの場合はゼロより大きくなければなりません。
  • この戦略は、新しい取引に入る前に反対のポジションを自動的に閉じることも、現在のポジションが横ばいになるまでシグナルを無視することもできます。また、単一位置モードを強制することもできます。
  • 取引は毎日の時間枠に制限できます。午前 0 時を超えるウィンドウがサポートされます。
  • エントリー後、ストラテジーはエントリー価格を保存し、設定されたポイント距離を価格オフセットに変換し、ストップロス、テイクプロフィット、トレーリングストップイベントのローソク足を監視します。

リスク管理

  • ストップロスとテイクプロフィットのレベルは、ポイント距離に商品価格ステップを乗じたエントリー価格から計算されます。
  • トレーリングストップは、価格が設定されたトレーリングステップだけ前進した後にアクティブになり、トレーリング距離で追従します。
  • プロテクションレベルに達すると、ポジションは成行注文で即座にクローズされ、すべてのリスクレベルがリセットされます。

パラメーター

  • ローソクの種類 – インジケーター計算用のローソクを構築するために使用される時間枠。
  • 高速期間/低速期間 – チャイキン オシレーターの高速期間と低速期間。
  • Signal MA Period / Signal MA Type – オシレーターに適用される平滑化移動平均設定。
  • ゼロレベルを使用 – 取引する前に両方のオシレーター値がゼロの正しい側にある必要があります。
  • 取引モード – ロングのみ、ショートのみ、または両方向を許可します。
  • リバースシグナル – ロングエントリーとショートエントリーを交換します。
  • 反対側を閉じる – 新しい取引を開始する前に、既存の反対側のポジションを閉じます。
  • 1 つのポジションのみ – ポジションがすでにオープンしている場合はエントリーを禁止します。
  • 時間制御 / 開始時間 / 終了時間の使用 – 毎日の取引ウィンドウを有効にして設定します。
  • ストップロス (ポイント) – 保護ストップのポイント単位の距離。
  • テイクプロフィット (pts) – 利益目標までのポイント単位の距離。
  • トレーリング ストップ (pts) – トレーリング ストップの距離 (ポイント単位)。
  • トレーリング ステップ (ポイント) – トレーリング ストップを移動する前の最小有利な動き (ポイント単位)。

追加メモ

  • 取引サイズを制御するには、戦略を開始する前に戦略の Volume プロパティを設定します。
  • この戦略は成行注文を発行するため、十分な流動性を確保し、実際の環境ではスリッページを考慮してください。
  • 取引ウィンドウの開始時間と終了時間が等しい場合、ストラテジーは非アクティブなままとなり、元の Expert Advisor の動作と一致します。
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>
/// Trades CCI oscillator zero-line crossovers with signal MA smoothing.
/// Originally based on Chaikin Oscillator concept, adapted to use CCI.
/// </summary>
public class ChoSmoothedEaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _maPeriod;

	private CommodityChannelIndex _cci;
	private readonly Queue<decimal> _cciHistory = new();
	private decimal? _prevCci;
	private decimal? _prevSignal;

	/// <summary>
	/// Initializes a new instance of the <see cref="ChoSmoothedEaStrategy"/> class.
	/// </summary>
	public ChoSmoothedEaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for signal calculations", "General");

		_cciPeriod = Param(nameof(CciPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "Period for CCI oscillator", "Indicator");

		_maPeriod = Param(nameof(MaPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Signal MA Period", "Period of smoothing moving average on CCI", "Indicator");
	}

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

	/// <summary>
	/// CCI oscillator period.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Period of the smoothing moving average.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

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

		_prevCci = null;
		_prevSignal = null;
		_cciHistory.Clear();

		_cci = new CommodityChannelIndex { Length = CciPeriod };

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

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

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

		_cciHistory.Enqueue(cciValue);
		while (_cciHistory.Count > MaPeriod)
			_cciHistory.Dequeue();

		if (!_cci.IsFormed)
			return;

		if (_cciHistory.Count < MaPeriod)
		{
			_prevCci = cciValue;
			return;
		}

		// Calculate signal line (SMA of CCI)
		var sum = 0m;
		var history = _cciHistory.ToArray();
		foreach (var v in history)
			sum += v;
		var signalValue = sum / history.Length;

		if (_prevCci is null || _prevSignal is null)
		{
			_prevCci = cciValue;
			_prevSignal = signalValue;
			return;
		}

		var crossUp = _prevCci <= _prevSignal && cciValue > signalValue;
		var crossDown = _prevCci >= _prevSignal && cciValue < signalValue;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		var minSpread = 25m;

		if (crossUp && Math.Abs(cciValue - signalValue) >= minSpread)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossDown && Math.Abs(cciValue - signalValue) >= minSpread)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevCci = cciValue;
		_prevSignal = signalValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_prevCci = null;
		_prevSignal = null;
		_cci = null;
		_cciHistory.Clear();

		base.OnReseted();
	}
}