GitHub で見る

Crypto Analysis戦略

概要

この戦略は、MetaTrader 4のエキスパートアドバイザー「Crypto Analysis」をStockSharpへ移植したものです。主取引時間軸で価格が外側のボリンジャーバンドに触れた後、マーケット構造が弱気(高速LWMAが低速LWMAを下回る)である状況のブレイクアウトを探します。上位時間軸のモメンタム急増と月次MACDフィルターが望む方向で一致した場合にのみ取引を許可します。市場に入った後は、元のEAを反映する階層的な保護ブロックでポジションを管理します。pipベースのストップ、金額ベースのトレーリング、ブレイクイーブン移動、ポートフォリオのドローダウン管理を含みます。

取引ロジック

  • シグナル時間軸: 設定可能(既定はM15)。すべてのエントリー/決済ルールはこのローソク足で評価されます。
  • ボラティリティトリガー: ロング準備では直前ローソク足の安値が下側ボリンジャーバンド(20, 2)に触れるか突き抜ける必要があります。上側バンドへの接触はショート準備になります。
  • トレンドフィルター: どちらのシナリオでも、高速線形加重移動平均(LWMA、既定6)が低速LWMA(既定85)より下にある必要があり、EAの弱気バイアス確認を再現します。
  • RSI確認: RSI(14)はロングで50より上、ショートで50より下である必要があります。
  • モメンタム急増: 上位時間軸の直近3つのMomentum(14)値について、100の基準線からの最大絶対偏差が買い/売りしきい値を超える必要があります。これはMQLコードで使われるモメンタムスパイクを捉えます。
  • 月次MACDフィルター: 別の月次(既定は30日足)MACD(12, 26, 9)が方向を確認します。ロングではMACDメインがシグナルを上回り、ショートではその逆を要求します。
  • エントリー実行: すべてのフィルターが一致すると、戦略は成行注文を開きます。反転前に反対ポジションを解消し、単一のネットポジションを保ちます。これはEAが反対取引を閉じる動作を反映します。

ポジション管理

  • 初期ストップと目標: 設定可能なpip距離は、EAと同じ5桁/3桁の扱いで銘柄のティックサイズから変換されます(0.000010.001のステップは10倍されます)。
  • トレーリングストップ: 新しい高値(ロング)または安値(ショート)が形成された後、ストップは常に到達済みの最良水準を尊重しながら、価格からTrailingStopPipsだけ後ろへ引き上げられます。
  • ブレイクイーブン: 利益がBreakEvenTriggerPipsに達すると、ストップはエントリー価格にBreakEvenOffsetPipsを加えた位置(ロング)または差し引いた位置(ショート)へ移動します。
  • 金額目標: 任意の絶対額またはパーセントベースの利益上限により、浮動PnLが指定水準に達した時点でポジションを閉じます。
  • 金額トレーリング: 含み益がMoneyTrailTargetを超えると、戦略はピークを追跡し、戻り幅がMoneyTrailStop以上になった場合にポジションを閉じます。
  • エクイティストップ: 浮動エクイティ(現在のポートフォリオ価値と未実現PnLの合計)を監視します。ピークからのドローダウンがEquityRiskPercentを超えると、ポジションを解消します。

複数時間軸データ

3つの購読が自動登録されます。

  1. ボリンジャー/LWMA/RSIルール用の主ローソク足系列。
  2. モメンタムフィルター用の上位時間軸ローソク足(既定はH1)。
  3. MACD確認用の月次ローソク足(既定は30日バー)。

パラメーター

パラメーター 説明
OrderVolume 基本注文サイズ。新しいポジションを開く前に反対ポジションを閉じます。
UseMoneyTakeProfit 絶対金額のテイクプロフィット目標を有効にします。
MoneyTakeProfit UseMoneyTakeProfitがtrueの場合に決済を起動するポートフォリオ通貨での利益。
UsePercentTakeProfit 初期エクイティから計算するパーセントベースのテイクプロフィット目標を有効にします。
PercentTakeProfit パーセント目標が有効なとき、ポジションを閉じるために必要な利益率。
EnableMoneyTrailing 金額ベースのトレーリングブロックを有効にします。
MoneyTrailTarget 金額トレーリングを開始する利益水準。
MoneyTrailStop MoneyTrailTarget到達後に許容する最大利益戻り幅。
StopLossPips 初期ストップロス距離(pips)。
TakeProfitPips 初期テイクプロフィット距離(pips)。
TrailingStopPips トレーリングストップ距離(pips)。
UseBreakEven ブレイクイーブンへのストップ移動を有効にします。
BreakEvenTriggerPips ブレイクイーブン保護を起動する前に必要なpip利益。
BreakEvenOffsetPips ブレイクイーブンストップ配置時にエントリー価格へ追加するpips。
FastMaPeriod 典型価格で計算する高速LWMAの長さ。
SlowMaPeriod 典型価格で計算する低速LWMAの長さ。
MomentumPeriod 上位時間軸のMomentum指標の期間。
MomentumBuyThreshold ロングエントリーに必要な最小モメンタム偏差。
MomentumSellThreshold ショートエントリーに必要な最小モメンタム偏差。
MacdFastLength 上位時間軸MACDフィルターの高速EMA長。
MacdSlowLength 上位時間軸MACDフィルターの低速EMA長。
MacdSignalLength 上位時間軸MACDフィルターのシグナル長。
UseEquityStop ポートフォリオのドローダウン保護を有効にします。
EquityRiskPercent ポジションを強制決済する前に許容するエクイティドローダウン率。
CandleType エントリーに使う主時間軸。
MomentumCandleType モメンタム確認に使う上位時間軸。
MacdCandleType MACD確認に使う上位時間軸。

注記

  • StockSharp版は単一のネットポジションを維持し、新しい取引の前に反対注文を閉じるEAと一致します。
  • すべての保護ルールは、元のスクリプトの「新しいバー」処理を再現するため、確定済みローソク足で動作します。
  • 標準的なpipサイズを持たない合成シンボルや銘柄を使う場合は、StopLossPipsおよび関連パラメーターを取引所のティック値に合わせて調整してください。
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 CryptoAnalysisStrategy : 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 CryptoAnalysisStrategy()
	{
		_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;
	}
}