GitHub で見る
毎日の STP エントリー フレーム戦略 (StockSharp)
概要
デイリー STP エントリー フレーム戦略 は、StockSharp の高レベル API を使用して、元の MetaTrader エキスパート アドバイザー「デイリー STP エントリー フレーム」の動作を複製します。システムは、新しい取引日の開始時にブレイクアウトストップ注文を準備します。エントリー価格は前日の高値と安値から導出され、追加のフィルターを使用して、注文を準備する前に市場がこれらの極端な値付近に位置していることを確認します。このロジックは、「ベースポイント」が 5 桁の相場の 1 ピップの 10 分の 1 に相当する外国為替スタイルの商品向けに調整されています。
コアワークフロー
- 毎日のレンジ追跡 – この戦略は、毎日のローソク足をサブスクライブして、前のセッションの高値と安値を記憶します。
- リアルタイム監視 – レベル 1 データは、日中管理のための現在の買値、売値、および最後の取引価格を提供します。
- 注文の準備 – 新しい日の始まりに、最後の価格が昨日の高値/安値から少なくとも
ThresholdPoints 離れていて、当日の始値がその極端な値の正しい側にある場合、逆指値注文が送信されます。
High + SpreadPoints / 2 でストップを購入します (価格単位に変換)。
Low - SpreadPoints / 2での販売ストップ。
- リスク検証 – 資本ドローダウンが
MaximumDrawdownPercent を超えるか、時間フィルター (週末、時間フィルター、または日フィルター) で取引が禁止されている場合、新しい注文はブロックされます。
- ポジション管理 – 取引がアクティブになると、戦略により次のことが強制されます。
- 静的なストップロスとテイクプロフィットの距離。
CloseAfterSeconds の後のオプションの時間ベースの終了。
- オリジナルの「SL スロープ」パラメータをエミュレートするオプションのトレーリング ストップ。
- 一日の終わりの衛生管理 – 保留中の注文は、
NoNewOrdersHour (または専用の金曜日の締め切り時間) 以降、および暦日が変わるたびにキャンセルされます。
取引ルール
- 長いエントリ
SideFilter が 0 (両方) または 1 (ロングのみ) の場合に許可されます。
- 前日の高値から現在の価格を引いた値 ≥
ThresholdPoints。
- 今日の始値は昨日の高値を下回っています。
- 計算されたエントリー価格は、現在のアスクからの最小距離を考慮する必要があります。
- 短いエントリー
SideFilter が 0 (両方) または -1 (短いのみ) の場合に許可されます。
- 現在の価格から前日の安値を引いた値 ≥
ThresholdPoints。
- 今日の始値は昨日の安値を上回っています。
- 計算されたエントリー価格は、現在の入札からの最小距離を考慮する必要があります。
- お金の管理
- 動的なボリューム サイジングでは、累積利益 (
PercentOfProfit) の割合が使用されます。
- 結果のサイズは
MinVolume と MaxVolume によって制限され、機器の VolumeStep に合わせられます。
- 測定されたドローダウンが
MaximumDrawdownPercent に達すると、取引は自動的に一時停止されます。
- 保護ロジック
- ストップロスとテイクプロフィットのレベルはベースポイントで表され、商品のピップサイズを使用して価格オフセットに変換されます。
- トレーリングストップは、
TrailingSlope < 1 の場合にのみアクティブになります。未実現利益が増加するにつれて、保護しきい値を価格に近づけます。
- ライフタイム出口は、設定された秒数が経過すると、開いているポジションを閉じます。
パラメーター
| 名前 |
説明 |
CandleType |
参照ローソク足を取得するために使用される時間枠 (デフォルトでは毎日)。 |
StopLossPoints |
ベースポイントでのストップロス距離。 |
TakeProfitPoints |
ベースポイントでのテイクプロフィット距離。 |
TrailingSlope |
トレーリング中に利益の一部が留保される。 ≥ 1 はこの機能を無効にします。 |
SideFilter |
-1 ショートのみ、0 両方向、1 ロングのみ。 |
ThresholdPoints |
現在の価格とストップを設定するために必要な以前の極値との間の最小ギャップ。 |
SpreadPoints |
スプレッドを補正するための追加のオフセット (極端値の上/下で半分が使用されます)。 |
SlippagePoints |
最小停止距離チェックに安全バッファが追加されました。 |
NoNewOrdersHour |
通常の日に保留中の注文をキャンセルする時間 (プラットフォーム時間)。 |
NoNewOrdersHourFriday |
金曜日特有のキャンセル時間。 |
EarliestOrderHour |
新しい注文を作成できる 1 日の最も早い時間。 |
DayFilter |
全日の場合は 6、日曜日から金曜日までの取引の場合は 0 ~ 5。 |
CloseAfterSeconds |
オプションの時間ベースの終了 (0 は無効)。 |
PercentOfProfit |
ポジションサイズを調整するために使用される累積利益の割合。 |
MinVolume / MaxVolume |
計算されたボリュームのハード境界。 |
MaximumDrawdownPercent |
新しい注文をブロックするドローダウンしきい値。 |
変換メモ
- Pip 変換は MetaTrader の実装を反映します。セキュリティが小数点以下 3 桁または 5 桁を公開する場合、基点は
PriceStep * 10 になります。
- 逆指値注文のキャンセルウィンドウは、金曜日の別の締め切りを含む、専門家の夕方の清掃を再現します。
- トレーリング ロジックは元の勾配公式 (ロングの場合は
newStop = Bid - StopLoss - Slope * (Bid - Entry)) に従います。
- MQL バージョンからの株式通知は戦略ログ メッセージに置き換えられます。
- StockSharp 実装は、ソースの動作と一致して、ポジションがオープンしている場合でも保留中の注文をアクティブに保ちます。
使用のヒント
- 正確なサイジングを確保するために、適切に構成された
PriceStep、StepPrice、および VolumeStep の値を外国為替商品に割り当てます。
- ライブ実行時には、この戦略と StockSharp のリスク管理 (ポートフォリオ制限、コネクタ レベルの保護) を組み合わせます。
- デザイナーまたはランナーを使用して
ThresholdPoints、TrailingSlope、および PercentOfProfit を最適化し、特定のシンボルに対するブレイクアウト感度を調整します。
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;
/// <summary>
/// Daily breakout strategy that enters on price crossing previous session extremes.
/// Converted from "Daily STP Entry Frame" MetaTrader expert.
/// Uses market orders when price breaks above previous high or below previous low.
/// </summary>
public class DailyStpEntryFrameStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private decimal _pipSize;
private decimal _stopLossOffset;
private decimal _takeProfitOffset;
private decimal? _previousDayHigh;
private decimal? _previousDayLow;
private decimal _currentDayHigh;
private decimal _currentDayLow;
private DateTime? _currentTradingDay;
private bool _tradedToday;
private decimal _entryPrice;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
public DailyStpEntryFrameStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time-frame for monitoring", "General");
_stopLossPoints = Param(nameof(StopLossPoints), 80m)
.SetNotNegative()
.SetDisplay("Stop-Loss (points)", "Stop-loss distance", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
.SetNotNegative()
.SetDisplay("Take-Profit (points)", "Take-profit distance", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_stopLossOffset = 0m;
_takeProfitOffset = 0m;
_previousDayHigh = null;
_previousDayLow = null;
_currentDayHigh = 0m;
_currentDayLow = 0m;
_currentTradingDay = null;
_tradedToday = false;
_entryPrice = 0m;
_longStop = null;
_longTake = null;
_shortStop = null;
_shortTake = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = Security?.PriceStep ?? 0.01m;
if (_pipSize <= 0) _pipSize = 0.01m;
_stopLossOffset = StopLossPoints * _pipSize;
_takeProfitOffset = TakeProfitPoints * _pipSize;
_previousDayHigh = null;
_previousDayLow = null;
_currentTradingDay = null;
_tradedToday = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var date = candle.OpenTime.Date;
// Track daily high/low
if (_currentTradingDay != date)
{
// Save previous day's range
if (_currentTradingDay != null)
{
_previousDayHigh = _currentDayHigh;
_previousDayLow = _currentDayLow;
}
_currentTradingDay = date;
_currentDayHigh = candle.HighPrice;
_currentDayLow = candle.LowPrice;
_tradedToday = false;
}
else
{
_currentDayHigh = Math.Max(_currentDayHigh, candle.HighPrice);
_currentDayLow = Math.Min(_currentDayLow, candle.LowPrice);
}
// Manage existing position
ManagePosition(candle);
// Check for breakout entries
if (_previousDayHigh is null || _previousDayLow is null)
return;
if (_tradedToday || Position != 0)
return;
var close = candle.ClosePrice;
// Breakout above previous day high => buy
if (close > _previousDayHigh.Value)
{
_entryPrice = close;
_longStop = _stopLossOffset > 0 ? close - _stopLossOffset : null;
_longTake = _takeProfitOffset > 0 ? close + _takeProfitOffset : null;
_shortStop = null;
_shortTake = null;
BuyMarket();
_tradedToday = true;
}
// Breakout below previous day low => sell
else if (close < _previousDayLow.Value)
{
_entryPrice = close;
_shortStop = _stopLossOffset > 0 ? close + _stopLossOffset : null;
_shortTake = _takeProfitOffset > 0 ? close - _takeProfitOffset : null;
_longStop = null;
_longTake = null;
SellMarket();
_tradedToday = true;
}
}
private void ManagePosition(ICandleMessage candle)
{
if (Position > 0)
{
if (_longStop is decimal stop && candle.LowPrice <= stop)
{
SellMarket();
_longStop = null;
_longTake = null;
return;
}
if (_longTake is decimal take && candle.HighPrice >= take)
{
SellMarket();
_longStop = null;
_longTake = null;
}
}
else if (Position < 0)
{
if (_shortStop is decimal stop && candle.HighPrice >= stop)
{
BuyMarket();
_shortStop = null;
_shortTake = null;
return;
}
if (_shortTake is decimal take && candle.LowPrice <= take)
{
BuyMarket();
_shortStop = null;
_shortTake = null;
}
}
}
}
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.Strategies import Strategy
class daily_stp_entry_frame_strategy(Strategy):
def __init__(self):
super(daily_stp_entry_frame_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Time-frame for monitoring", "General")
self._stop_loss_points = self.Param("StopLossPoints", 80.0) \
.SetDisplay("Stop-Loss (points)", "Stop-loss distance", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 200.0) \
.SetDisplay("Take-Profit (points)", "Take-profit distance", "Risk")
self._prev_day_high = None
self._prev_day_low = None
self._cur_day_high = 0.0
self._cur_day_low = 0.0
self._current_day = None
self._traded_today = False
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def StopLossPoints(self):
return float(self._stop_loss_points.Value)
@property
def TakeProfitPoints(self):
return float(self._take_profit_points.Value)
def OnStarted2(self, time):
super(daily_stp_entry_frame_strategy, self).OnStarted2(time)
self._pip_size = 0.01
sec = self.Security
if sec is not None:
ps = sec.PriceStep
if ps is not None and float(ps) > 0:
self._pip_size = float(ps)
self._sl_offset = self.StopLossPoints * self._pip_size
self._tp_offset = self.TakeProfitPoints * self._pip_size
self._prev_day_high = None
self._prev_day_low = None
self._current_day = None
self._traded_today = False
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
dt = candle.OpenTime
day = dt.Date
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._current_day is None or self._current_day != day:
if self._current_day is not None:
self._prev_day_high = self._cur_day_high
self._prev_day_low = self._cur_day_low
self._current_day = day
self._cur_day_high = high
self._cur_day_low = low
self._traded_today = False
else:
if high > self._cur_day_high:
self._cur_day_high = high
if low < self._cur_day_low:
self._cur_day_low = low
# manage position
if self.Position > 0:
if self._long_stop is not None and low <= self._long_stop:
self.SellMarket()
self._long_stop = None
self._long_take = None
return
if self._long_take is not None and high >= self._long_take:
self.SellMarket()
self._long_stop = None
self._long_take = None
return
elif self.Position < 0:
if self._short_stop is not None and high >= self._short_stop:
self.BuyMarket()
self._short_stop = None
self._short_take = None
return
if self._short_take is not None and low <= self._short_take:
self.BuyMarket()
self._short_stop = None
self._short_take = None
return
# entries
if self._prev_day_high is None or self._prev_day_low is None:
return
if self._traded_today or self.Position != 0:
return
if close > self._prev_day_high:
self._entry_price = close
self._long_stop = close - self._sl_offset if self._sl_offset > 0 else None
self._long_take = close + self._tp_offset if self._tp_offset > 0 else None
self._short_stop = None
self._short_take = None
self.BuyMarket()
self._traded_today = True
elif close < self._prev_day_low:
self._entry_price = close
self._short_stop = close + self._sl_offset if self._sl_offset > 0 else None
self._short_take = close - self._tp_offset if self._tp_offset > 0 else None
self._long_stop = None
self._long_take = None
self.SellMarket()
self._traded_today = True
def OnReseted(self):
super(daily_stp_entry_frame_strategy, self).OnReseted()
self._prev_day_high = None
self._prev_day_low = None
self._cur_day_high = 0.0
self._cur_day_low = 0.0
self._current_day = None
self._traded_today = False
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
def CreateClone(self):
return daily_stp_entry_frame_strategy()