GitHub で見る

ドンチェーンカウンターチャネルシステム

概要

Donchain Counter-Channel System は、Michal Rutka による 2005 年の MetaTrader 4 エキスパート アドバイザーを再現しています。毎日のローソク足で計算された 20 日間の Donchian チャネルのターンを監視します。下のバンドが上昇に転じた場合、この戦略は売り手が価格を新たな安値に押し上げることに失敗したと想定し、市場で次のセッションを購入します。上部バンドが下降に転じると、戦略はそれを上昇局面での勢いの損失と解釈し、市場で空売りします。保護停止は常に反対側の Donchian バンドと位置合わせされるため、出口は元の停止管理ロジックを反映します。

エントリは 24 時間ごとに 1 つだけ許可され、システムを 1 日あたり最大 1 つの注文に制限する記事のルールと一致します。この実装では、StockSharp の高レベルの API をインジケーター バインディングとともに使用するため、完成した各ローソク足とともに Donchian の値が到着します。

取引ロジック

  1. 設定された CandleType をサブスクライブし (デフォルトでは毎日)、選択した ChannelPeriod を使用して DonchianChannels インジケーターを評価します。
  2. ろうそくが終わるたびに:
    • ロングポジションがオープンしている場合は、ストップレベルが上昇したときにストップレベルを現在の下限バンドに移動し、ローソク足の安値がそのレベルに触れたら終了します。
    • ショートポジションがオープンしている場合は、ストップレベルが下落したときにストップレベルを現在の上部バンドに移動し、ローソク足の高値がそのレベルに触れたら終了します。
    • ポジションがない場合は、最後の取引が TradeCooldown より前に発生したときのエントリーをスキップします。
    • 前のローソク足の下側の Donchian バンドがその前のローソク足よりも高い場合は、ロングになり、チャネル底部の上昇を示します。初期停止を現在の低域バンドに設定します。
    • 前のローソク足の上部 Donchian バンドがその前のローソク足よりも低い場合はショートに進み、チャネル天井の下降を示します。初期停止を現在の上部バンドに設定します。
  3. 価格が反転してポジションが決済されるまで、バンドに沿ってストップを追跡し続けます。

パラメーター

名前 デフォルト 説明
Volume 1 長いエントリと短いエントリの両方のサイズを注文します。
ChannelPeriod 20 Donchian の上部バンドと下部バンドを計算するために使用されるローソク足の数。
TradeCooldown 1 day 新しいエントリが許可されるまでの最小待機期間。
CandleType Daily Donchian チャネルが計算されるローソク足シリーズ。

指標とデータ

  • Donchian チャネル – トレンド転換の検出とトレーリングストップに使用されるチャネルの上限と下限を提供します。
  • 毎日のキャンドル (デフォルト) – 24 時間のクールダウンとインジケーターのターンの評価に必要な終了時間を提供します。

実装メモ

  • この戦略では、BindEx を使用して、ローソク足ハンドラーで型指定された DonchianChannelsValue を受け取り、両方のバンドが同時に利用できるようにします。
  • オリジナルの EA が新しいバーごとにストップロスを更新したのと同じように、保存されたバンド値に対してローソク足の高値と安値を監視することでストップがシミュレートされます。
  • クールダウン タイマーは新しいエントリの場合にのみ更新され、同じ取引日内の複数のエントリを防止するソース スクリプトを反映しています。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Donchian counter-channel system.
/// Counter-trend: buys when lower channel turns up (reversal from support).
/// Sells when upper channel turns down (reversal from resistance).
/// </summary>
public class DonchainCounterChannelSystemStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _prevPrevHigh;
	private decimal _prevPrevLow;
	private int _barCount;

	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public DonchainCounterChannelSystemStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 20)
			.SetDisplay("Channel Period", "Donchian channel lookback", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevHigh = 0m; _prevLow = 0m; _prevPrevHigh = 0m; _prevPrevLow = 0m; _barCount = 0; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_barCount = 0;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, ProcessCandle)
			.Start();
	}

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

		_barCount++;

		if (_barCount < 3)
		{
			_prevPrevHigh = _prevHigh;
			_prevPrevLow = _prevLow;
			_prevHigh = highest;
			_prevLow = lowest;
			return;
		}

		// Lower band turning up = support holding = buy signal
		var lowerTurningUp = lowest > _prevLow && _prevLow <= _prevPrevLow;
		// Upper band turning down = resistance holding = sell signal
		var upperTurningDown = highest < _prevHigh && _prevHigh >= _prevPrevHigh;

		if (lowerTurningUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (upperTurningDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevPrevHigh = _prevHigh;
		_prevPrevLow = _prevLow;
		_prevHigh = highest;
		_prevLow = lowest;
	}
}