GitHub で見る

Millenium Code戦略

Millenium Code戦略は、1日に最大1回の取引を開く位置取引システムです。方向は、最近の高値と安値でフィルタリングされた移動平均クロスオーバーによって決定されます。取引はユーザーが定義した時間に置かれ、時間、ストップロス、テイクプロフィット、または最大期間によってクローズされます。

取引ロジック

  1. 指定された開始時間に、戦略は現在の曜日に取引が許可されているかどうかを確認します。
  2. 速い単純移動平均と遅い単純移動平均が比較されます。速いMAが遅いMAを上回り、価格がブレイクアウトを確認した場合、ロングポジションが開かれます。逆の条件でショートポジションが開かれます。
  3. 1日に1回の取引のみ許可されます。次の取引日まで、後続のシグナルは無視されます。
  4. 以下の場合にポジションがクローズされます:
    • ストップロスまたはテイクプロフィットのレベルに達した場合。
    • 設定されたクローズ時間が来た場合。
    • 最大取引期間を超えた場合。

パラメーター

  • Candle Type – 入力ローソク足の時間軸。
  • Fast MA – 速い移動平均の期間。
  • Slow MA – 遅い移動平均の期間。
  • HighLow Bars – 最近の高値と安値を検索するために使用するローソク足の数。
  • Reverse – 買い/売りシグナルを反転します。
  • Stop Loss – 価格ステップ単位のストップロスまでの距離。
  • Take Profit – 価格ステップ単位のテイクプロフィットまでの距離。
  • Open Hour/Minute – エントリーを探し始める時間(-1で無効)。
  • Close Hour/Minute – ポジションをクローズする時間(-1で無効)。
  • Duration – 最大取引期間(時間単位、0で無効)。
  • Sunday ... Friday – 各曜日の取引を有効にします。

注意事項

この戦略は高レベルAPIの機能のみを使用し、インジケーターの履歴に直接アクセスすることを避けています。教育的な例として意図されており、投資アドバイスではありません。

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>
/// Millenium Code positional strategy.
/// Uses fast/slow MA crossover with high/low channel filter.
/// </summary>
public class MilleniumCodeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _highLowBars;
	private readonly StrategyParam<bool> _reverseSignal;
	private readonly StrategyParam<decimal> _stopLossPct;
	private readonly StrategyParam<decimal> _takeProfitPct;

	private Highest _highest;
	private Lowest _lowest;
	private decimal _prevFast;
	private decimal _prevSlow;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
	public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
	public int HighLowBars { get => _highLowBars.Value; set => _highLowBars.Value = value; }
	public bool ReverseSignal { get => _reverseSignal.Value; set => _reverseSignal.Value = value; }
	public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
	public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }

	public MilleniumCodeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast MA", "Fast moving average length", "Indicators");
		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow MA", "Slow moving average length", "Indicators");
		_highLowBars = Param(nameof(HighLowBars), 10)
			.SetDisplay("HighLow Bars", "Bars count for high/low search", "Indicators");
		_reverseSignal = Param(nameof(ReverseSignal), true)
			.SetDisplay("Reverse", "Reverse buy/sell logic", "General");
		_stopLossPct = Param(nameof(StopLossPct), 2m)
			.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
		_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
			.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_highest = default;
		_lowest = default;
		_prevFast = 0m;
		_prevSlow = 0m;
	}

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

		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		_highest = new Highest { Length = HighLowBars };
		_lowest = new Lowest { Length = HighLowBars };

		Indicators.Add(_highest);
		Indicators.Add(_lowest);

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, (candle, fastVal, slowVal) =>
		{
			if (candle.State != CandleStates.Finished)
				return;

			var highResult = _highest.Process(candle);
			var lowResult = _lowest.Process(candle);

			if (!highResult.IsFormed || !lowResult.IsFormed)
			{
				_prevFast = fastVal;
				_prevSlow = slowVal;
				return;
			}

			var high = highResult.ToDecimal();
			var low = lowResult.ToDecimal();

			if (_prevFast == 0 || _prevSlow == 0)
			{
				_prevFast = fastVal;
				_prevSlow = slowVal;
				return;
			}

			var crossUp = _prevFast < _prevSlow && fastVal > slowVal;
			var crossDown = _prevFast > _prevSlow && fastVal < slowVal;

			var dir = 0;
			if (crossUp) dir = ReverseSignal ? -1 : 1;
			else if (crossDown) dir = ReverseSignal ? 1 : -1;

			if (dir == 1 && Position <= 0)
			{
				if (Position < 0) BuyMarket();
				BuyMarket();
			}
			else if (dir == -1 && Position >= 0)
			{
				if (Position > 0) SellMarket();
				SellMarket();
			}

			_prevFast = fastVal;
			_prevSlow = slowVal;
		}).Start();

		StartProtection(
			takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
			stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
			useMarketOrders: true);

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