GitHub で見る
1-2-3 パターン戦略
概要
この戦略は、Martes による MetaTrader 4 エキスパート アドバイザー「1-2-3_forCodeBase_v01.mq4」を再現します。完了したローソク足をスキャンして、古典的な 1-2-3 反転パターン、つまり 2 つの連続するトレンド レッグが 3 番目のリトレースメント レッグによって完了するかどうかをスキャンします。ポートは、カスタム トレンド長インジケーター (RelDownTrLen_forCodeBase_v01 および RelUpTrLen_forCodeBase_v01) や MACD 確認ロジックを含む、元のシステムのすべてのルールを保持します。
長いセットアップでは、現在の価格に近い新しい谷 (ポイント 3)、その前のピーク (ポイント 2)、および古い谷 (ポイント 1) が必要です。以前の下降トレンドは現在の上昇リトレースメントより少なくとも TrendRatio 倍長くなければならず、MACD はポイント 3 でプラスのままシグナルライン (またはゼロ) を上回らなければなりません。短いサイドは山と谷が反転したこれらのチェックを反映しています。ストップはポイント 3 の 1 ポイントを超えて配置され、テイクプロフィットは前のスイングの高さに等しく、オプションのピップベースのトレーリングストップは、トレードが利益に転じると出口を引き締めます。
取引ルール
- 設定されたローソク足シリーズ (
CandleType) をサブスクライブし、終値の MACD (速い/遅い/シグナル期間) を計算します。
- ローソク体のローリング履歴を維持して、1-2-3 構造を検出します。谷はローソク体の極小値であり、ピークは極大値です。
- MQL インジケーターからの凸包法を使用して、カスタム トレンド長指標を評価します。
TrendRatio に従って、最新の下降トレンドの長さ ([0,1] にスケール) が前の上昇トレンドを支配する必要があります (ショートの場合はその逆)。
- MACD で設定を確認します。
- ロング:
MACD は信号の上 (またはゼロの上) と交差し、ポイント 3 の MACD の値は正です。
- 短い:
MACD は信号の下 (またはゼロの下) を横切り、ポイント 3 の MACD の値は負です。
- 追加のエントリフィルター:
- 現在の価格からポイント 2 までの距離は 5 ポイント以内でなければなりません。
- 予測停止距離 (
|point2 - point3|) は少なくとも 13 ポイントである必要があります。
TakeProfitPips は 10 以上でなければなりません。それ以外の場合、取引は無効になります (元の安全性チェックが反映されます)。
- 注文処理:
BuyMarket/SellMarket を使用して、TradeVolume ロットでエントリーします(反転の場合は現在のポジション量と集計されます)。
- 最初のストップロス = ポイント 3 ± 1 価格ステップ。
- テイクプロフィット = エントリー ±
|point2 - point3|。
TrailingStopPips > 0 の場合、含み益がトレーリング距離を超えると、そのポイントだけストップをトレーリングします。
- ストップ、利益確定、またはトレーリングストップで終了します。一度にオープンできるポジションは 1 つだけです。
パラメーター
| パラメータ |
種類 |
デフォルト |
説明 |
TakeProfitPips |
decimal |
60 |
EA の互換性フラグ。値が 10 未満に設定された場合、取引は停止します。 |
TradeVolume |
decimal |
0.5 |
各成行注文に使用される MetaTrader ロットのボリューム。 |
TrailingStopPips |
decimal |
30 |
トレーリング ストップの距離 (MetaTrader ポイント)。末尾を無効にするには、0 に設定します。 |
TrendRatio |
decimal |
4 |
以前のメイントレンドの長さと最近のリトレースメントの間の最小比率。 |
CandleType |
DataType |
H1 |
パターンと MACD の計算に使用されるローソク足シリーズ。 |
MacdFast |
int |
12 |
MACD オシレーターの高速な EMA 周期。 |
MacdSlow |
int |
26 |
MACD オシレーターの遅い EMA 周期。 |
MacdSignal |
int |
9 |
信号線 EMA 期間。 |
PatternLookback |
int |
100 |
1-2-3 ポイントを見つけるときにスキャンされる履歴ローソクの最大数。 |
実装メモ
- オリジナルのカスタムインジケーターはそのまま移植されています。凸包検索はローソク体の最長の単調セグメントを計算し、それらの相対的な長さを
[0,1] で返します。これらの値はトレンド比率フィルターを駆動します。
- 過去のローソク足と MACD の値は、ルックバックに十分な深さを維持しながら過剰なメモリ使用を回避するために、境界付きバッファー (600 要素) に保存されます。
- ストップとターゲットは、MetaTrader の動作に合わせて手動で管理されます。価格はローソク足の高値/安値と比較され、価格が設定された距離以上進んだ場合にのみトレーリングストップが厳しくなります。
Volume はリセット時と起動時に TradeVolume と同期されるため、最適化は標準の戦略プロパティに依存できます。
参考資料
- 元の MQL4 エキスパートアドバイザー:
MQL/8131/1-2-3_forCodeBase_v01.mq4。
- カスタム インジケーター:
RelDownTrLen_forCodeBase_v01.mq4、RelUpTrLen_forCodeBase_v01.mq4。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class OneTwoThreePatternStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevMid; private bool _hasPrev;
private int _cooldown;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OneTwoThreePatternStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 20).SetDisplay("Channel Period", "Pattern lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMid = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevMid = mid;
return;
}
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevMid = mid;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class one_two_three_pattern_strategy(Strategy):
def __init__(self):
super(one_two_three_pattern_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 20).SetDisplay("Channel Period", "Pattern lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0; self._prev_mid = 0.0; self._has_prev = False; self._cooldown = 0
@property
def channel_period(self): return self._channel_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(one_two_three_pattern_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_mid = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(one_two_three_pattern_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
highest = Highest(); highest.Length = self.channel_period
lowest = Lowest(); lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close; self._prev_mid = mid; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_close = close; self._prev_mid = mid; return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume); self._cooldown = 6
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume); self._cooldown = 6
self._prev_close = close; self._prev_mid = mid
def CreateClone(self): return one_two_three_pattern_strategy()