GitHub で見る

Puria Method戦略

概要

Puria Method戦略は、もともとMetaTrader用に設計されたトレンドフォローシステムです。3つの移動平均とMACDトレンドフィルターを組み合わせて、Momentumブレイクアウトを検出します。StockSharpへの変換では元のエントリーロジックを維持しつつ、部分的な利益確定や自動トレーリングストップなどの最新のリスク管理機能を追加しています。

トレードロジック

  • 設定可能な平滑化手法と価格ソースを使用して3つの移動平均を計算します。
  • 前のバーにおける、遅い基準MAと2つの速いMAの差を評価します。強気シグナルは両方の速いMAが基準より少なくとも0.5ポイント上にある必要があります。弱気シグナルは基準が同じマージンでリードする必要があります。
  • MACD主線で市場の方向性を確認します。ロングトレードは前のMACD値が正で、設定されたバー数で直近のMACDヒストリーが非減少であることが必要です。ショートトレードは逆の条件が必要です。
  • エントリーが発動すると、戦略は反対のポジション(あれば)を決済し、シグナル方向に新しいネットポジションを開きます。

リスク管理

  • ストップロス / テイクプロフィット: 価格はエントリーからpips距離で計算され、インストゥルメントの価格ステップに正規化されます。
  • トレーリングストップ: ポジションがトレーリング閾値にステップを加えた値を超えると、追加のトレーリングステップごとにストップが前進します。
  • 部分決済: 価格が最小利益距離を超えると、利益を確保するために設定可能な割合のポジションが決済されます。
  • ポジション管理: アルゴリズムはエントリー後の最高値(ロング)または最安値(ショート)を追跡し、ローソク足がそのレベルを突破したときにストップまたは利益ルールを発動します。

パラメーター

名前 説明
StopLossPips pips単位のストップロス距離。
TakeProfitPips pips単位のテイクプロフィット距離。
TrailingStopPips pips単位のトレーリングストップ距離。
TrailingStepPips トレーリングストップが更新される前の最小利益前進量。
MinProfitStepPips 部分利益確定前の最小距離(pips)。
MinProfitFraction 最小利益ステップ達成時に決済するポジションの割合。
CandleType 戦略が使用するプライマリローソク足シリーズ。
Ma0Period, Ma1Period, Ma2Period 3つの移動平均の期間。
Ma0Shift, Ma1Shift, Ma2Shift 各移動平均に適用されるオプションのバーシフト。
Ma0Method, Ma1Method, Ma2Method 移動平均の平滑化手法(単純、指数、平滑化、線形加重)。
Ma0Price, Ma1Price, Ma2Price 移動平均のローソク足価格ソース。
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod MACDの設定。
MacdTrendBars MACDの単調トレンドを確認するために使用するバー数(最小3)。
MacdPrice MACD計算のローソク足価格ソース。

注意事項

  • 戦略は未完成のローソク足データへの依存を避けるため、MAとMACDの比較には前の完成したバーを使用します。
  • pipsサイズはインストゥルメントの価格ステップと小数精度から自動的に導出されます。
  • トレーリングと部分決済機能はゼロ以外の設定値が必要です。そうでない場合、対応するブロックは非アクティブのままです。
  • 変換版は完成したローソク足(CandleStates.Finished)のみに依存し、元のチャートのタイムフレームと一致するローソク足シリーズと組み合わせて使用する必要があります。
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 PuriaMethodStrategy : 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 PuriaMethodStrategy()
	{
		_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;
	}
}