GitHub で見る
受注安定化戦略
概要
注文安定化戦略は、MetaTrader エキスパート アドバイザー hjueiisyx8lp2o379e_www_forex-instruments_info.mq4 の転換です。元のロボットは、現在の価格付近で 2 つの逆指値注文を出し、ブレイクアウトを待ちます。ポジションがオープンされると、システムは最近のローソク体を監視して価格行動が停滞(「安定化」)したかどうかを判断し、市場の勢いが失われたとき、または事前に定義された利益しきい値に達したときに取引を終了します。
この C# ポートは、高レベルの StockSharp API を使用して同じロジックを維持します。生のティックではなく完成したローソク足に依存するため、バックテストやライブ取引中の動作が決定的になります。
取引ルール
- オープンポジションやアクティブな注文がない場合、この戦略は市場より上に買いストップを、市場より下に売りストップを送信します。距離は MetaTrader ポイント (通常は 1 ピップに等しい) で測定されます。
- 逆指値注文が実行された場合:
- 約定された注文により、
OrderVolume ロットのポジションがオープンされます。
- 反対方向のブレイクアウトをキャッチするために、反対のストップ注文が保留されたままになります。
- ポジションがオープンしている間、戦略は直近に終了した 2 つのローソク足の実体サイズをチェックします。
- 最新のローソク足本体が
StabilizationPoints より小さく、変動利益が ProfitThreshold より高い場合、ポジションはクローズされ、反対の未決注文はキャンセルされます。
- 2 つの連続するローソク足が
StabilizationPoints より小さい場合、現在の利益に関係なく取引は終了します。
- 利益が
AbsoluteFixation に達した場合、取引は直ちに終了します。
- 保留中の注文は、値がゼロ (無期限) に設定されていない限り、
ExpirationMinutes の後にキャンセルされ、再作成されます。
パラメーター
| 名前 |
説明 |
デフォルト |
OrderVolume |
両方のストップエントリーに使用されるロットの取引量。 |
0.1 |
OrderDistancePoints |
現在の終値と各逆指値注文の間の距離。MetaTrader ポイントで表されます。 |
20 |
ProfitThreshold |
安定化によるエグジットが許可される前に必要な最低変動利益(口座通貨)。 |
-2 |
AbsoluteFixation |
即時退出を強制する利益レベル(口座通貨)。 |
30 |
StabilizationPoints |
市場が横ばいであることを示すローソク足の最大サイズ(ポイント)。 |
25 |
ExpirationMinutes |
保留中のストップ注文の存続期間 (分単位)。 0 は有効期限を無効にします。 |
20 |
CandleType |
安定化の評価に使用されるローソクのタイプ (デフォルトは 5 分の時間枠)。 |
TimeFrame(5m) |
変換メモ
- 元のエキスパートアドバイザーはチャートティックに基づいて操作していました。このポートは完成したキャンドルのみを評価し、再現可能なバックテストを確保しながらロジックを維持します。
- MetaTrader 個の「ポイント」は StockSharp
PriceStep にマッピングされます。商品に価格ステップがない場合、1 のステップが想定されます。
- 利益は、価格変動をアカウント通貨に換算するために
PriceStep と StepPrice を使用して概算されます。
- すべてのコード コメントは英語で書き直され、パラメーターのメタデータにはグループ化されたわかりやすい説明が含まれています。
使用法
- 戦略を StockSharp ソリューションに追加し、必要なセキュリティとポートフォリオを割り当てます。
- パラメータ、特にローソク足の時間枠とポイント単位の距離を機器の特性に合わせて設定します。
- 戦略を開始します。ペアのストップ注文を発行し、上記の安定化ロジックに従ってポジションを管理します。
さらなるアイデア
- 応答性とノイズ フィルターのバランスをとるために、さまざまなキャンドル間隔を試してください。
- この戦略とボラティリティ フィルター (ATR、Bollinger バンド) を組み合わせて、非常に静かなセッションでの取引を回避します。
- 絶対利益目標に近づいたら、トレーリング ストップまたは部分的なポジション エグジットを使用してロジックを拡張します。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Order stabilization strategy - trades breakouts after periods of low volatility.
/// When candle body is small (stabilization), waits for a directional breakout.
/// Goes long on bullish breakout candle after stabilization, short on bearish.
/// </summary>
public class OrderStabilizationStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stabilizationFactor;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevBody;
private decimal _prevAtr;
private bool _hasPrev;
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal StabilizationFactor { get => _stabilizationFactor.Value; set => _stabilizationFactor.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OrderStabilizationStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR period for volatility", "Indicators");
_stabilizationFactor = Param(nameof(StabilizationFactor), 0.5m)
.SetDisplay("Stabilization Factor", "Body must be less than ATR * factor for stabilization", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBody = 0m;
_prevAtr = 0m;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var threshold = atrValue * StabilizationFactor;
if (!_hasPrev)
{
_prevBody = body;
_prevAtr = atrValue;
_hasPrev = true;
return;
}
var prevThreshold = _prevAtr * StabilizationFactor;
var wasStabilized = _prevBody < prevThreshold;
// After stabilization, trade breakout candle
if (wasStabilized && body > threshold)
{
var bullish = candle.ClosePrice > candle.OpenPrice;
if (bullish && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (!bullish && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
_prevBody = body;
_prevAtr = atrValue;
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class order_stabilization_strategy(Strategy):
"""Trades breakouts after periods of low volatility (stabilization).
When previous candle body is small relative to ATR, waits for directional breakout.
Goes long on bullish breakout candle, short on bearish."""
def __init__(self):
super(order_stabilization_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for volatility", "Indicators")
self._stabilization_factor = self.Param("StabilizationFactor", 0.5) \
.SetDisplay("Stabilization Factor", "Body must be less than ATR * factor for stabilization", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_body = 0.0
self._prev_atr = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@property
def StabilizationFactor(self):
return self._stabilization_factor.Value
def OnReseted(self):
super(order_stabilization_strategy, self).OnReseted()
self._prev_body = 0.0
self._prev_atr = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(order_stabilization_strategy, self).OnStarted2(time)
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr_value)
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
body = abs(close - open_price)
factor = float(self.StabilizationFactor)
threshold = atr_val * factor
if not self._has_prev:
self._prev_body = body
self._prev_atr = atr_val
self._has_prev = True
return
prev_threshold = self._prev_atr * factor
was_stabilized = self._prev_body < prev_threshold
# After stabilization, trade breakout candle
if was_stabilized and body > threshold:
bullish = close > open_price
if bullish and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif not bullish and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_body = body
self._prev_atr = atr_val
def CreateClone(self):
return order_stabilization_strategy()