GitHub で見る

Micro Trend Breakouts戦略

概要

Micro Trend Breakouts戦略は、MetaTrader エキスパートアドバイザー "Micro Trend Breakouts" を StockSharp の高レベル API に変換したものです。線形加重移動平均、momentum の急増、MACD の整列を使って短命のブレイクアウトパターンを検出します。戦略は一度に最大 1 つのポジションだけを開き、エントリーとエグジットのトリガーにはローソク足の終値を使います。

インジケーター

  • 線形加重移動平均 (LWMA) - 分析時間枠上に構築された高速平均と低速平均が、支配的な市場方向をフィルターします。
  • Momentum - 直近 3 本の確定ローソク足にわたる絶対 momentum 読み取り値が、価格がブレイクアウト方向へ加速していることを確認するため、設定可能なしきい値を超える必要があります。
  • MACD - クラシックな MACD ヒストグラムを方向フィルターとして使います (ロングではメインラインがシグナルより上、ショートでは下)。

エントリーロジック

  1. 設定された時間枠の確定ローソク足を待ちます。
  2. ロングでは高速 LWMA が低速 LWMA より上にあることを要求します (ショートでは下)。
  3. 小さなブレイクアウト構造を確認します。ロングでは 2 バー前のローソク足の安値が前のローソク足の高値を下回っている必要があります (ショートでは反転)。
  4. momentum 加速を要求します。直近 3 つの絶対 momentum 値のいずれかが設定しきい値を超える必要があります。
  5. MACD の整列を検証します。
    • ロング: MACD メインラインは、ゼロより上か下かに関係なく、シグナルラインより上にある必要があります。
    • ショート: MACD メインラインは、ゼロラインの位置に関係なく、シグナルラインより下にある必要があります。

すべてのチェックが一致すると、戦略はデフォルト数量パラメーターを使って成行注文を発行します。

エグジットロジックとリスク管理

  • 初期 stop-loss と take-profit 水準は価格ステップで表され、エントリー時に計算されます。値をゼロにすると、対応する水準は無効になります。
  • 任意の breakeven モジュールは、価格が設定ステップ数だけ進んだ後、必要に応じて安全パディングを加えながらストップをエントリー価格へ近づけます。
  • trailing 保護は、利益のある動きの後にストップを引き締めることができます。利益が起動しきい値を超えると、ストップはエントリー後に見た最高 (ロング) または最低 (ショート) のローソク足価格から trailing 距離だけ離れて追随します。
  • ポジションのエグジットは、確定した各ローソク足で評価されます。価格が stop-loss または take-profit 水準に到達すると、戦略は成行注文でポジションを閉じ、内部状態をリセットします。

パラメーター

名前 説明 デフォルト
Order Volume エントリーに使う成行注文数量。 1
Candle Type 価格分析の時間枠。 15m time frame
Fast LWMA 高速線形加重移動平均の期間。 6
Slow LWMA 低速線形加重移動平均の期間。 85
Momentum Period momentum インジケーターのルックバック。 14
Momentum Threshold 直近 3 本のローソク足で必要な最小絶対 momentum。 0.3
MACD Fast / Slow / Signal MACD が使用する移動平均期間。 12 / 26 / 9
Stop Loss 価格ステップ単位の stop 距離。0 は stop を無効にします。 20
Take Profit 価格ステップ単位の目標距離。0 は目標を無効にします。 50
Use Trailing trailing stop ロジックを有効にします。 true
Trail Activation trailing stop が有効になる前に必要なステップ単位の利益。 40
Trail Step 極値と trailing stop の間のステップ単位の距離。 40
Use Breakeven breakeven stop 調整を有効にします。 true
Breakeven Trigger breakeven モジュールを準備するステップ単位の利益。 30
Breakeven Padding stop を breakeven に移動するときに追加するステップ。 30

注意事項

  • 戦略は単一のローソク足ストリームを購読し、低レベル API 呼び出しを避けるため、高レベルフレームワーク要件内に収まります。
  • 保護注文は取引に直接添付されません。代わりに、戦略は StartProtection() と組み合わせたローソク足ベースの監視を使い、基底クラスがオープンポジションを監督するようにします。
  • C# コード内のすべての inline コメントは、変換ガイドラインに従って英語で書かれています。
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;

public class MicroTrendBreakoutsStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MicroTrendBreakoutsStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}