GitHub で見る
トレンド発見戦略
概要
Trend Finderは、元のエキスパートアドバイザー TREND FINDER.mq4 から変換されたマルチタイムフレームのトレンドフォロー戦略です。ロジックはStockSharpの高レベルAPIを使用し、線形加重移動平均を上位タイムフレームのモメンタムおよびMACDフィルターからの確認と組み合わせるという核心的なアイデアを維持しています。この戦略は、持続的な高値または安値に続くブレイクアウトを検出することに焦点を当て、モメンタムと長期トレンドの一致が確認されたらブレイクアウトの方向にエントリーすることを目指しています。
市場データとインジケーター
- ベースタイムフレーム (
CandleType) – パターン認識と注文実行に使用する主要ローソク足。線形加重移動平均はこれらのローソク足の典型的な価格で計算されます。
- モメンタムタイムフレーム (
MomentumCandleType) – 中立値100からのモメンタム偏差を評価するために使用する上位タイムフレームのローソク足。取引を許可する前に、直近3つのモメンタム読み取り値が設定可能な閾値を超える必要があります。
- MACDタイムフレーム (
MacdCandleType) – カスタマイズ可能なfast、slow、signalの長さを持つMACDで処理される長期ローソク足。ロング(ショート)設定には強気(弱気)のMACD条件が必要です。
エントリーロジック
- トレンドブレイクアウト検出 – 戦略は最大過去100本のローソク足(最新3本を除く)をスキャンして、最高値または最安値を見つけます。強気の設定は、現在のバーが以前の高値クラスターの上でオープンし、前の3つの高値のうち少なくとも1つがその歴史的レベルより下に留まることを必要とします。弱気の設定は安値に対してロジックを反転させます。
- 移動平均の整列 – ロングには速いLWMAが遅いLWMAの上にある必要があり、ショートには下にある必要があります。
- 最近のローソク足構造 – ロングには2バー前の安値が前のバーの高値より低い必要があり (
Low[2] < High[1])、ショートには最新の安値が2バー前の高値より低い必要があります (Low[1] < High[2])。これは元の価格構造チェックを保持します。
- モメンタム確認 – 上位タイムフレームでの最後の3つのモメンタム偏差(|Momentum – 100|として計算)のうち少なくとも1つが、設定された買い/売りの閾値を超える必要があります。
- MACD確認 – 長期タイムフレームでの最新のMACD値が、ロングにはシグナルラインの上にある必要があり、ショートには下にある必要があります。
- ポジションフィルタリング – 新しいロング注文は現在のポジションが非正の場合にのみ発行され、新しいショート注文は非負の場合にのみ発行されます。注文ボリュームは
Volume + |Position| に等しく、素早いポジション反転をサポートします。
エグジットとリスク管理
- ストップロス (
StopLoss) – ロング(ショート)ポジションのエントリー価格より下(上)の固定距離。
- テイクプロフィット (
TakeProfit) – 固定の利益目標;到達したらポジションは直ちにクローズされます。
- トレーリングストップ (
TrailingStop) – ロングへのエントリー後に到達した最高値、またはショートの最安値を追います。ストップは完了したローソク足ごとに調整されます。
- ブレークイーブン (
BreakEvenTrigger, BreakEvenOffset) – 価格がトリガー距離だけトレードに有利な方向に動いたら、保護ストップがロング(ショート)のエントリー価格プラス(マイナス)オフセットに移動し、価格が戻した場合に利益が確保されます。
- 自動フラット化 – ヘルパーメソッドがポジションサイズ全体をクローズし、すべてのトラッキング変数をリセットします。この実装には部分的なエグジットはありません。
パラメーター
| パラメーター |
説明 |
デフォルト |
|
|
CandleType |
パターン認識と注文実行のベースタイムフレーム。 |
15分ローソク足 |
|
|
MomentumCandleType |
モメンタム確認の計算に使用する上位タイムフレーム。 |
1時間ローソク足 |
|
|
MacdCandleType |
MACD確認のタイムフレーム(デフォルト約30日)。 |
30日ローソク足 |
|
|
FastMaLength |
速い線形加重移動平均の長さ。 |
6 |
|
|
SlowMaLength |
遅い線形加重移動平均の長さ。 |
85 |
|
|
MomentumPeriod |
モメンタム比率に使用する上位タイムフレームのバー数。 |
14 |
|
|
MomentumThresholdBuy |
ロングエントリーを許可するために必要な最小 |
Momentum − 100 |
。 |
0.3 |
MomentumThresholdSell |
ショートエントリーを許可するために必要な最小 |
Momentum − 100 |
。 |
0.3 |
MacdShortLength |
MACD計算内の速いEMAの長さ。 |
12 |
|
|
MacdLongLength |
MACD計算内の遅いEMAの長さ。 |
26 |
|
|
MacdSignalLength |
MACDのシグナルEMAの長さ。 |
9 |
|
|
StopLoss |
計器価格単位での絶対ストップロス距離。 |
0.0020 |
|
|
TakeProfit |
計器価格単位での絶対テイクプロフィット距離。 |
0.0050 |
|
|
TrailingStop |
有利な動きに追従するトレーリングストップ距離。 |
0.0040 |
|
|
BreakEvenTrigger |
ブレークイーブンストップを有効にする利益距離。 |
0.0030 |
|
|
BreakEvenOffset |
ブレークイーブンが有効になったら適用される追加オフセット。 |
0.0010 |
|
|
注意: 戦略を開始する前に Strategy.Volume プロパティを希望の注文サイズに設定してください。上記のパラメーターは絶対価格単位で表されています;取引する計器のティックサイズに応じて調整してください。
使用ガイドライン
- 戦略を希望する
Security に割り当て、Portfolio と Volume のプロパティを設定します。
- 選択したデータソースが要求された3つのローソク足タイムフレームすべてを提供できることを確認してください;そうでなければ、確認フィルターが準備完了になりません。
- 計器のボラティリティに合わせてリスクパラメーターを調整します。デフォルトは絶対価格距離として表されているため、株式、先物、または暗号通貨の場合はスケール変更が必要になる場合があります。
- オプションで生成されたチャートエリアをアタッチして、価格、トレード、両方の移動平均を視覚化します。
- 注文確認のためにログを監視します。戦略はエントリーとエグジットに成行注文(
BuyMarket、SellMarket)を使用します。
元のエキスパートアドバイザーとの違い
- MQLスクリプトに存在する資本ベースのストップ、残高ベースのテイクプロフィットロジック、プッシュ/メール通知は、戦略をコアトレードルールに集中させ、StockSharpの高レベルAPIと整合させるために意図的に省略されました。
- ボリューム管理は簡略化されています:StockSharpバージョンは一度に最大1つのネットポジションを開き、設定された
Volume を使用してトレードをサイジングします。
- 口座通貨で表された資金管理パラメーターは複製されません;代わりに、価格ベースのリスクコントロール(
StopLoss、TakeProfit、TrailingStop、ブレークイーブン)が提供されます。
推奨される機能拡張
- 複数のシンボルを同時に取引する場合は、ポートフォリオレベルのリスクコントロールを追加します。
- 非流動性期間中のトレードを無効にするために、セッションフィルターまたはボラティリティフィルターと組み合わせます。
- そのような機能が必要な場合は、外部分析(例:資本追跡)にフィルを送信することを検討してください。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class TrendFinderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public TrendFinderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 25).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class trend_finder_strategy(Strategy):
def __init__(self):
super(trend_finder_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 25) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(trend_finder_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(trend_finder_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return trend_finder_strategy()