E-News Lucky戦略
概要
E-News Lucky戦略は、MetaTraderのエキスパートアドバイザーe-News-LuckyのStockSharpポートです。このシステムは古典的なニュースブレイクアウトアプローチを自動化します:
- 設定可能な
PlacementTimeで、現在価格周辺にDistancePipsだけオフセットした買いストップと売りストップの両方の注文を送信します。 - いずれかの待機注文が執行されると、反対の注文は即座にキャンセルされます。設定されたpipオフセットに従って初期保護のストップロスとテイクプロフィットレベルが付加されます。
TrailingStopPipsとTrailingStepPipsを介してトレーリングストップを有効にし、取引が有利な方向に動くにつれて利益を確保できます。- 設定された
CancelTimeに、残っているすべての待機注文が削除され、オープンポジションは取引ウィンドウ外でのリスク保有を避けるために閉じられます。
戦略は予定された時間を追跡しトレーリングストップを更新するためだけにキャンドルデータ(CandleType、デフォルト1分足)を使用します。インジケーターの計算には依存しません。
パラメーター
| 名前 | 説明 |
|---|---|
Volume |
各待機エントリーの注文量。戦略はこのボリュームで対称的な買いストップと売りストップ注文を送信します。 |
StopLossPips |
エントリー価格と保護ストップロスの間の距離(pips単位)。ストップを無効にするにはゼロに設定。 |
TakeProfitPips |
エントリー価格と利益目標の間の距離(pips単位)。目標を無効にするにはゼロに設定。 |
TrailingStopPips |
pips単位のトレーリングストップ距離。この値がゼロより大きい場合のみトレーリングエンジンがアクティブになります。 |
TrailingStepPips |
トレーリングストップが再び移動する前に必要な最小pip利益。レンジ相場でのストップ更新過多を防ぎます。 |
DistancePips |
ストップ注文を配置するために使用される現在価格からのオフセット(pips単位)。 |
PlacementTime |
待機注文が配置されるブローカー/サーバー時間の時刻。デフォルト:10:30。 |
CancelTime |
待機注文がキャンセルされ、オープンポジションが閉じられる時刻。デフォルト:22:30。 |
CandleType |
スケジューリングとトレーリングに使用するキャンドルシリーズ。デフォルト:1分足。 |
実装上の注意
- pip サイズはMetaTraderのロジックに従います:シンボルが3桁または5桁を持つ場合、戦略はpip単位で作業するために価格ステップに10を掛けます。
- すべての価格は注文送信前にインストゥルメントの価格ステップに正規化されます。
- トレーリングストップは最後のクローズを
PositionPriceと比較し、利益がTrailingStopPipsとTrailingStepPipsの両方を超えたときのみ保護ストップを移動します。 - 待機注文は配置時間に達したとき、毎取引日に再作成されます。キャンセル時間のチェックにより、ウィンドウの終了時にすべての建玉がフラットになることが保証されます。
使用のヒント
- 戦略を流動性の高いタイトなスプレッドのインストゥルメントに適用してください;ブレイクアウト距離はニュース的な価格動向を前提としています。
- 対象となる経済カレンダーに従って
PlacementTimeとCancelTimeを設定してください。 - インストゥルメントのボラティリティに合わせてpip距離を調整してください。値が大きいほど誤ったトリガーの可能性が減り、小さな値はより早い動きを捉えられますがwhipsawリスクが増加します。
- 固定ストップが好ましい場合は
TrailingStopPipsをゼロに保つことでトレーリングを無効にできます。 - 高インパクトのニュース時にスリッページとスプレッドを監視し、待機注文が期待通りに約定されることを確認してください。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Scheduled breakout strategy that monitors price around a reference level and enters on breakout.
/// Converted from the original pending-order version to use market orders.
/// </summary>
public class ENewsLuckyStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly StrategyParam<decimal> _distancePips;
private readonly StrategyParam<int> _placementHour;
private readonly StrategyParam<int> _cancelHour;
private readonly StrategyParam<DataType> _candleType;
private decimal _pipSize;
private decimal? _buyLevel;
private decimal? _sellLevel;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private bool _pendingActive;
private bool _lastWasPlacementDay;
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
public decimal TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
public decimal DistancePips
{
get => _distancePips.Value;
set => _distancePips.Value = value;
}
public int PlacementHour
{
get => _placementHour.Value;
set => _placementHour.Value = value;
}
public int CancelHour
{
get => _cancelHour.Value;
set => _cancelHour.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ENewsLuckyStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 50m)
.SetDisplay("Stop Loss", "Stop loss in pips", "Trading");
_takeProfitPips = Param(nameof(TakeProfitPips), 150m)
.SetDisplay("Take Profit", "Take profit in pips", "Trading");
_trailingStopPips = Param(nameof(TrailingStopPips), 5m)
.SetDisplay("Trailing Stop", "Trailing distance in pips", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 5m)
.SetDisplay("Trailing Step", "Minimum trailing step in pips", "Risk");
_distancePips = Param(nameof(DistancePips), 20m)
.SetGreaterThanZero()
.SetDisplay("Entry Distance", "Distance from market in pips", "Trading");
_placementHour = Param(nameof(PlacementHour), 2)
.SetDisplay("Placement Hour", "Hour to set breakout levels", "General");
_cancelHour = Param(nameof(CancelHour), 22)
.SetDisplay("Cancel Hour", "Hour to cancel and close", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_buyLevel = null;
_sellLevel = null;
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
_pendingActive = false;
_lastWasPlacementDay = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var step = Security?.PriceStep ?? 0m;
_pipSize = step > 0 ? step : 1m;
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.CloseTime.Hour;
var price = candle.ClosePrice;
// Set breakout levels at placement hour
if (hour == PlacementHour && !_lastWasPlacementDay && Position == 0)
{
var distance = DistancePips * _pipSize;
_buyLevel = price + distance;
_sellLevel = price - distance;
_pendingActive = true;
_lastWasPlacementDay = true;
}
if (hour != PlacementHour)
_lastWasPlacementDay = false;
// Cancel at cancel hour
if (hour == CancelHour && _pendingActive)
{
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
return;
}
// Check breakout triggers
if (_pendingActive && Position == 0)
{
if (_buyLevel.HasValue && candle.HighPrice >= _buyLevel.Value)
{
var buyLevel = _buyLevel.Value;
BuyMarket(Volume);
_entryPrice = buyLevel;
_stopPrice = StopLossPips > 0 ? _entryPrice - StopLossPips * _pipSize : null;
_takePrice = TakeProfitPips > 0 ? _entryPrice + TakeProfitPips * _pipSize : null;
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
}
else if (_sellLevel.HasValue && candle.LowPrice <= _sellLevel.Value)
{
var sellLevel = _sellLevel.Value;
SellMarket(Volume);
_entryPrice = sellLevel;
_stopPrice = StopLossPips > 0 ? _entryPrice + StopLossPips * _pipSize : null;
_takePrice = TakeProfitPips > 0 ? _entryPrice - TakeProfitPips * _pipSize : null;
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
}
}
// Manage open position
if (Position > 0)
{
// Trailing stop
if (TrailingStopPips > 0 && _entryPrice > 0)
{
var trailDist = TrailingStopPips * _pipSize;
var stepDist = TrailingStepPips * _pipSize;
if (price - _entryPrice > trailDist + stepDist)
{
var newStop = price - trailDist;
if (!_stopPrice.HasValue || newStop > _stopPrice.Value)
_stopPrice = newStop;
}
}
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Position);
ResetPosition();
return;
}
if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
{
SellMarket(Position);
ResetPosition();
}
}
else if (Position < 0)
{
// Trailing stop
if (TrailingStopPips > 0 && _entryPrice > 0)
{
var trailDist = TrailingStopPips * _pipSize;
var stepDist = TrailingStepPips * _pipSize;
if (_entryPrice - price > trailDist + stepDist)
{
var newStop = price + trailDist;
if (!_stopPrice.HasValue || newStop < _stopPrice.Value)
_stopPrice = newStop;
}
}
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(-Position);
ResetPosition();
return;
}
if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
{
BuyMarket(-Position);
ResetPosition();
}
}
}
private void ResetPosition()
{
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan, Math
class e_news_lucky_strategy(Strategy):
def __init__(self):
super(e_news_lucky_strategy, self).__init__()
self._stop_loss_pips = self.Param("StopLossPips", 50.0)
self._take_profit_pips = self.Param("TakeProfitPips", 150.0)
self._trailing_stop_pips = self.Param("TrailingStopPips", 5.0)
self._trailing_step_pips = self.Param("TrailingStepPips", 5.0)
self._distance_pips = self.Param("DistancePips", 20.0)
self._placement_hour = self.Param("PlacementHour", 2)
self._cancel_hour = self.Param("CancelHour", 22)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._pip_size = 0.0
self._buy_level = None
self._sell_level = None
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._pending_active = False
self._last_was_placement_day = False
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(e_news_lucky_strategy, self).OnStarted2(time)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
self._pip_size = step if step > 0 else 1.0
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
hour = candle.CloseTime.Hour
price = float(candle.ClosePrice)
# Set breakout levels at placement hour
if hour == self._placement_hour.Value and not self._last_was_placement_day and self.Position == 0:
distance = self._distance_pips.Value * self._pip_size
self._buy_level = price + distance
self._sell_level = price - distance
self._pending_active = True
self._last_was_placement_day = True
if hour != self._placement_hour.Value:
self._last_was_placement_day = False
# Cancel at cancel hour
if hour == self._cancel_hour.Value and self._pending_active:
self._pending_active = False
self._buy_level = None
self._sell_level = None
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
return
# Check breakout triggers
if self._pending_active and self.Position == 0:
if self._buy_level is not None and float(candle.HighPrice) >= self._buy_level:
buy_level = self._buy_level
self.BuyMarket(self.Volume)
self._entry_price = buy_level
self._stop_price = self._entry_price - self._stop_loss_pips.Value * self._pip_size if self._stop_loss_pips.Value > 0 else None
self._take_price = self._entry_price + self._take_profit_pips.Value * self._pip_size if self._take_profit_pips.Value > 0 else None
self._pending_active = False
self._buy_level = None
self._sell_level = None
elif self._sell_level is not None and float(candle.LowPrice) <= self._sell_level:
sell_level = self._sell_level
self.SellMarket(self.Volume)
self._entry_price = sell_level
self._stop_price = self._entry_price + self._stop_loss_pips.Value * self._pip_size if self._stop_loss_pips.Value > 0 else None
self._take_price = self._entry_price - self._take_profit_pips.Value * self._pip_size if self._take_profit_pips.Value > 0 else None
self._pending_active = False
self._buy_level = None
self._sell_level = None
# Manage open position
if self.Position > 0:
# Trailing stop for long
if self._trailing_stop_pips.Value > 0 and self._entry_price > 0:
trail_dist = self._trailing_stop_pips.Value * self._pip_size
step_dist = self._trailing_step_pips.Value * self._pip_size
if price - self._entry_price > trail_dist + step_dist:
new_stop = price - trail_dist
if self._stop_price is None or new_stop > self._stop_price:
self._stop_price = new_stop
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket(self.Position)
self._reset_position()
return
if self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket(self.Position)
self._reset_position()
elif self.Position < 0:
# Trailing stop for short
if self._trailing_stop_pips.Value > 0 and self._entry_price > 0:
trail_dist = self._trailing_stop_pips.Value * self._pip_size
step_dist = self._trailing_step_pips.Value * self._pip_size
if self._entry_price - price > trail_dist + step_dist:
new_stop = price + trail_dist
if self._stop_price is None or new_stop < self._stop_price:
self._stop_price = new_stop
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket(abs(self.Position))
self._reset_position()
return
if self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket(abs(self.Position))
self._reset_position()
def _reset_position(self):
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(e_news_lucky_strategy, self).OnReseted()
self._pip_size = 0.0
self._buy_level = None
self._sell_level = None
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._pending_active = False
self._last_was_placement_day = False
def CreateClone(self):
return e_news_lucky_strategy()