GitHub で見る
サンプルチェック未決注文戦略
サンプルチェック保留注文戦略では、ブック内に 1 つの買いストップ注文と 1 つの売りストップ注文が確実に存在することが継続的に保証されます。 Tungman によるオリジナルの MetaTrader 5 の専門家は、ブローカーが要求されたロット サイズを受け入れ、両方向に十分な空き証拠金があることを確認し、現在の買値/売値に加えて 1 日の有効期限を持つ新しい未決注文を送信します。この変換では、StockSharp の高レベルの注文管理 API とレベル 1 の見積もりを使用して同じワークフローが再現されます。
取引ロジック
- 市場データ処理
- この戦略はレベル 1 更新をサブスクライブし、最新の最良入札価格と最良売値をキャッシュします。
- 取引ロジックは、ブックの両側が判明し、環境の準備ができていること (戦略が実行されている、ポートフォリオが接続されているなど) を
IsFormedAndOnlineAndAllowTrading() が確認するまで一時停止されます。
- ボリュームの検証
- 受信ティックごとに、構成された
OrderVolume の Security.MinVolume、Security.MaxVolume、および Security.VolumeStep に対する検証がトリガーされます。
- このチェックは MT5 ヘルパーを反映しています。ボリュームは許容範囲内にあり、ステップの正確な倍数である必要があります。違反があると情報ログ エントリが生成され、新しい注文がブロックされます。
- 証拠金の事前チェック
- 何かを送信する前に、この戦略は、設定されたサイズのロングまたはショート ポジションを置くのに必要な証拠金を推定します。最新の買値/売値、商品乗数、ユーザー指定の
AccountLeverage を使用して要件を計算します。
- 現在または初期のポートフォリオ値がいずれかの方向に対して不十分な場合、アルゴリズムはそのティックに対して中止され、
CheckMoneyForTrade の保護手段を厳密に模倣します。
- 注文保留中
- アクティブな買いストップ注文が存在しない場合、新しい注文が現在のアスクで登録されます (最も近いティックに四捨五入されます)。同じルールが現在の入札での売りストップにも適用されます。どちらのオーダーも同じボリューム検証結果を再利用します。
- 有効期限は手動で適用されます。各注文には期限が保存されます (
ExpirationMinutes、デフォルトでは 1 日)。今後のティックは、期限を過ぎた場合に注文をキャンセルし、新しい未決注文のスロットをすぐにクリアします。
- リスク管理
StartProtection は、StopLossPoints と TakeProfitPoints に基づいて絶対的なストップロスとテイクプロフィットを配線します。注文がトリガーされると、StockSharp は設定された距離で保護出口を自動的に送信し、MT5 バージョンで使用される SL/TP パラメータを再作成します。
最終的には、2 つのストップ注文の間で常に市場を「ボックス化」した状態に保つ、最小限のブレイクアウト エンジンが完成します。一方の注文が約定されると、もう一方の注文はアクティブなままとなり、戦略が次回の相場更新時に不足しているレッグを再発行する準備をします。
パラメーター
| パラメータ |
説明 |
OrderVolume |
各ストップ注文とともに送信されるロットサイズ。ブローカーの制限とボリュームステップを尊重する必要があります。 |
StopLossPoints |
取引開始後の保護ストップの価格単位に変換されたポイント単位の距離。 |
TakeProfitPoints |
約定後に作成される利益ターゲットに使用されるポイント単位の距離。 |
ExpirationMinutes |
各未決注文の有効期間。期間が経過すると注文はキャンセルされ、次のティックで再作成されます。 |
AccountLeverage |
各送信前に証拠金要件を概算するために使用される推定アカウント レバレッジ。 |
すべての距離は、Security.PriceStep を使用して実際の価格オフセットに変換されます。商品が有効な価格ステップまたは乗数を公開していない場合、戦略は値 1 に戻り、定義された計算を維持します。ログメッセージには異常な構成が記録されるため、オペレータはパラメータを迅速に調整できます。
実装メモ
- 注文ライフサイクル – この戦略は、
BuyStop と SellStop によって返された最新の Order オブジェクトを追跡します。ヘルパー メソッドは、注文が Done、Canceled、または Failed に移行すると参照を破棄し、古い注文がアクティブな注文と間違われないようにします。
- 有効期限の処理 – StockSharp 取引所は、ストップ注文のサーバー側の有効期限を全面的にサポートしていません。この戦略はブローカー固有のフィールドに依存するのではなく、ローカルでタイムスタンプを監視し、未決注文が期限を過ぎた場合に
CancelOrder を呼び出します。
- マージンの概算 – 利用可能なマージンは、ポートフォリオの資本と設定されたレバレッジを使用して推定されます。これにより、取引所固有の実装を必要とせずに、動作を
OrderCalcMargin に近づけることができます。
- 高レベルの API の使用方法 – すべての操作は高レベルの
SubscribeLevel1、BuyStop、SellStop、および StartProtection ヘルパーに依存します。これらのヘルパーは変換ガイドラインに一致し、コードを簡潔に保ちます。
このドキュメントには、トレーダーが変換のあらゆるニュアンスを理解し、自信を持ってパラメータをブローカー環境に適応できるように、意図的に広範囲にわたる詳細が含まれています。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Sample Check Pending Order strategy: CCI trend following.
/// Buys when CCI crosses above zero, sells when CCI crosses below zero.
/// </summary>
public class SampleCheckPendingOrderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _cciLevel;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevCci;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public SampleCheckPendingOrderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 30)
.SetGreaterThanZero()
.SetDisplay("Period", "CCI period", "Indicators");
_cciLevel = Param(nameof(CciLevel), 100m)
.SetDisplay("CCI Level", "CCI crossover threshold", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var cci = new CommodityChannelIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevCci <= -CciLevel && cciValue > -CciLevel && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevCci >= CciLevel && cciValue < CciLevel && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevCci = cciValue;
_hasPrev = true;
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class sample_check_pending_order_strategy(Strategy):
def __init__(self):
super(sample_check_pending_order_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 30)
self._cci_level = self.Param("CciLevel", 100.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_cci = 0.0
self._candles_since_trade = 4
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 Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def CciLevel(self):
return self._cci_level.Value
@CciLevel.setter
def CciLevel(self, value):
self._cci_level.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(sample_check_pending_order_strategy, self).OnReseted()
self._prev_cci = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
def OnStarted2(self, time):
super(sample_check_pending_order_strategy, self).OnStarted2(time)
self._prev_cci = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
cci = CommodityChannelIndex()
cci.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self._process_candle).Start()
def _process_candle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
cci_val = float(cci_value)
if self._has_prev:
if self._prev_cci <= -self.CciLevel and cci_val > -self.CciLevel and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif self._prev_cci >= self.CciLevel and cci_val < self.CciLevel and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_cci = cci_val
self._has_prev = True
def CreateClone(self):
return sample_check_pending_order_strategy()