日足ブレイクアウト戦略
この戦略は日足の始値からのブレイクアウトをトレードします。新しい日の始まりに始値が記録されます。価格がこのレベルからユーザー定義のポイント数だけ離れ、前のバーが設定可能なサイズ範囲内にある場合、戦略はブレイクアウトの方向に参入します。
エントリーロジック
- 前のバーが上昇バーで、価格が日足始値を Break Point ポイント上回ると、ロングポジションが開かれます。
- 前のバーが下降バーで、価格が日足始値を Break Point ポイント下回ると、ショートポジションが開かれます。
- 前のバーのサイズは Last Bar Min から Last Bar Max ポイントの間でなければなりません。
- ブレイクアウトレベルは前のバーの実体内にある必要があります。
リスク管理
- オプションの Take Profit と Stop Loss はエントリー価格からのポイントで測定されます。
- Trailing Start、Trailing Stop、Trailing Step パラメーターでトレーリングストップを有効にできます。価格が Trailing Start ポイント有利に動いたとき、ストップはエントリーから Trailing Stop ポイントに設定され、その後 Trailing Step の増分でトレールします。
パラメーター
| 名前 | 説明 |
|---|---|
| Candle Type | 処理する足の時間軸。 |
| Break Point | トレードをトリガーする日足始値からの距離(ポイント)。 |
| Last Bar Min | 前のバーの最小サイズ(ポイント)。 |
| Last Bar Max | 前のバーの最大サイズ(ポイント)。 |
| Trailing Start | トレーリングストップを開始する価格変動(ポイント)。 |
| Trailing Stop | 初期トレーリング距離(ポイント)。 |
| Trailing Step | トレーリングストップを移動するステップ(ポイント)。 |
| Take Profit | テイクプロフィット距離(ポイント)。 |
| Stop Loss | ストップロス距離(ポイント)。 |
注意事項
この戦略は完成した足のみで動作し、エントリーとエグジットに成行注文を使用します。前のバーデータとトレーリングストップレベルの内部変数を保存します。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Daily-open breakout in security price steps, gated by the preceding
/// completed candle's body. Fixed protection uses actual fills and the optional
/// trailing exit is evaluated only on subsequent completed candles.
/// </summary>
public class DailyBreakpointStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _breakPoint, _lastBarMin, _lastBarMax;
private readonly StrategyParam<decimal> _takeProfit, _stopLoss;
private readonly StrategyParam<decimal> _trailingStart, _trailingStop, _trailingStep;
private decimal _dailyOpen, _previousOpen, _previousClose;
private DateTimeOffset _previousTime;
private bool _hasPrevious;
private decimal? _entryPrice, _bestPrice, _trailPrice;
private decimal _trackedPosition;
private Order _pending;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal BreakPoint { get => _breakPoint.Value; set => _breakPoint.Value = value; }
public decimal LastBarMin { get => _lastBarMin.Value; set => _lastBarMin.Value = value; }
public decimal LastBarMax { get => _lastBarMax.Value; set => _lastBarMax.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal TrailingStart { get => _trailingStart.Value; set => _trailingStart.Value = value; }
public decimal TrailingStop { get => _trailingStop.Value; set => _trailingStop.Value = value; }
public decimal TrailingStep { get => _trailingStep.Value; set => _trailingStep.Value = value; }
public DailyBreakpointStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Signal timeframe", "General");
_breakPoint = Param(nameof(BreakPoint), 30m).SetNotNegative()
.SetDisplay("Break Point", "Daily-open breakout offset in price steps", "Trading");
_lastBarMin = Param(nameof(LastBarMin), 5m).SetNotNegative()
.SetDisplay("Last Bar Min", "Minimum previous candle body in price steps", "Trading");
_lastBarMax = Param(nameof(LastBarMax), 100m).SetNotNegative()
.SetDisplay("Last Bar Max", "Maximum previous candle body in price steps", "Trading");
_takeProfit = Param(nameof(TakeProfit), 200m).SetNotNegative()
.SetDisplay("Take Profit", "Optional target in price steps", "Risk");
_stopLoss = Param(nameof(StopLoss), 100m).SetNotNegative()
.SetDisplay("Stop Loss", "Optional risk in price steps", "Risk");
_trailingStart = Param(nameof(TrailingStart), 0m).SetNotNegative()
.SetDisplay("Trailing Start", "Favorable move activating trailing in steps", "Risk");
_trailingStop = Param(nameof(TrailingStop), 0m).SetNotNegative()
.SetDisplay("Trailing Stop", "Trail distance; zero disables trailing", "Risk");
_trailingStep = Param(nameof(TrailingStep), 1m).SetGreaterThanZero()
.SetDisplay("Trailing Step", "Minimum improvement before a trail update", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
private void ResetState()
{
_dailyOpen = _previousOpen = _previousClose = 0m;
_previousTime = default;
_hasPrevious = false;
_entryPrice = _bestPrice = _trailPrice = null;
_trackedPosition = 0m;
_pending = null;
}
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var step = Security.PriceStep is decimal valid && valid > 0m ? valid : 1m;
StartProtection(
takeProfit: new Unit(TakeProfit * step, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss * step, UnitTypes.Absolute),
useMarketOrders: true);
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
protected override void OnNewPosition(Position position)
{
base.OnNewPosition(position);
ObservePosition(position);
}
protected override void OnPositionChanged(Position position)
{
base.OnPositionChanged(position);
ObservePosition(position);
}
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
ObservePosition(position);
}
private void ObservePosition(Position position)
{
if (position.Security != Security || position.Portfolio != Portfolio) return;
var volume = position.CurrentValue ?? 0m;
if (volume == 0m)
{
_entryPrice = _bestPrice = _trailPrice = null;
_trackedPosition = 0m;
return;
}
if (position.AveragePrice is not decimal avg || avg <= 0m) return;
if (_entryPrice == avg && _trackedPosition * volume > 0m)
{
_trackedPosition = volume;
return;
}
_entryPrice = _bestPrice = avg;
_trailPrice = null;
_trackedPosition = volume;
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished) return;
// The daily open is pinned to the very first completed candle of that day.
var isNewDay = !_hasPrevious || candle.OpenTime.Date != _previousTime.Date;
if (isNewDay) _dailyOpen = candle.OpenPrice;
var step = Security.PriceStep is decimal valid && valid > 0m ? valid : 1m;
var price = candle.ClosePrice;
if (IsFormedAndOnlineAndAllowTrading() &&
(_pending is null || _pending.State is OrderStates.Done or OrderStates.Failed))
{
if (Position != 0m && _entryPrice is decimal entry && TrailingStop > 0m)
{
var isLong = Position > 0m;
var best = _bestPrice ?? entry;
best = isLong ? Math.Max(best, price) : Math.Min(best, price);
_bestPrice = best;
if ((isLong ? best - entry : entry - best) >= TrailingStart * step)
{
var candidate = isLong ? best - TrailingStop * step : best + TrailingStop * step;
if (_trailPrice is not decimal current ||
(isLong ? candidate >= current + TrailingStep * step
: candidate <= current - TrailingStep * step))
_trailPrice = candidate;
}
if (_trailPrice is decimal trail && (isLong ? price <= trail : price >= trail))
_pending = isLong ? SellMarket(Math.Abs(Position)) : BuyMarket(Math.Abs(Position));
}
else if (Position == 0m && !isNewDay && _hasPrevious && _previousTime.Date == candle.OpenTime.Date)
{
var range = Math.Abs(_previousClose - _previousOpen);
var offset = BreakPoint * step;
var buyBreak = _dailyOpen + offset;
var sellBreak = _dailyOpen - offset;
var validSize = range >= LastBarMin * step && range <= LastBarMax * step;
if (validSize && _previousClose > _previousOpen && price > buyBreak &&
buyBreak >= _previousOpen && buyBreak <= _previousClose)
_pending = BuyMarket(Volume);
else if (validSize && _previousClose < _previousOpen && price < sellBreak &&
sellBreak <= _previousOpen && sellBreak >= _previousClose)
_pending = SellMarket(Volume);
}
}
_previousOpen = candle.OpenPrice;
_previousClose = candle.ClosePrice;
_previousTime = candle.OpenTime;
_hasPrevious = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, OrderStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class daily_breakpoint_strategy(Strategy):
"""Daily-open breakout with previous candle body and price-step protections."""
def __init__(self):
super(daily_breakpoint_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay(
"Candle Type", "Signal timeframe", "General")
self._break_point = self.Param("BreakPoint", 30.0).SetNotNegative().SetDisplay(
"Break Point", "Daily-open breakout offset in price steps", "Trading")
self._last_bar_min = self.Param("LastBarMin", 5.0).SetNotNegative().SetDisplay(
"Last Bar Min", "Minimum previous candle body in steps", "Trading")
self._last_bar_max = self.Param("LastBarMax", 100.0).SetNotNegative().SetDisplay(
"Last Bar Max", "Maximum previous candle body in steps", "Trading")
self._take_profit = self.Param("TakeProfit", 200.0).SetNotNegative().SetDisplay(
"Take Profit", "Optional target in price steps", "Risk")
self._stop_loss = self.Param("StopLoss", 100.0).SetNotNegative().SetDisplay(
"Stop Loss", "Optional risk in price steps", "Risk")
self._trailing_start = self.Param("TrailingStart", 0.0).SetNotNegative().SetDisplay(
"Trailing Start", "Activation gain in price steps", "Risk")
self._trailing_stop = self.Param("TrailingStop", 0.0).SetNotNegative().SetDisplay(
"Trailing Stop", "Trail distance; zero disables trailing", "Risk")
self._trailing_step = self.Param("TrailingStep", 1.0).SetGreaterThanZero().SetDisplay(
"Trailing Step", "Minimum improvement to adjust trailing", "Risk")
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_state(self):
self._daily_open = self._previous_open = self._previous_close = 0.0
self._previous_time = None
self._has_previous = False
self._entry_price = self._best_price = self._trail_price = None
self._tracked_position = 0.0
self._pending = None
def OnReseted(self):
super(daily_breakpoint_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(daily_breakpoint_strategy, self).OnStarted2(time)
self._reset_state()
step = float(self.Security.PriceStep or 1)
if step <= 0:
step = 1
self.StartProtection(
takeProfit=Unit(float(self._take_profit.Value) * step, UnitTypes.Absolute),
stopLoss=Unit(float(self._stop_loss.Value) * step, UnitTypes.Absolute),
useMarketOrders=True)
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def OnNewPosition(self, position):
super(daily_breakpoint_strategy, self).OnNewPosition(position)
self._observe_position(position)
def OnPositionChanged(self, position):
super(daily_breakpoint_strategy, self).OnPositionChanged(position)
self._observe_position(position)
def OnPositionReceived(self, position):
super(daily_breakpoint_strategy, self).OnPositionReceived(position)
self._observe_position(position)
def _observe_position(self, position):
if position.Security != self.Security or position.Portfolio != self.Portfolio:
return
volume = float(position.CurrentValue or 0)
if volume == 0:
self._entry_price = self._best_price = self._trail_price = None
self._tracked_position = 0.0
return
if position.AveragePrice is None or position.AveragePrice <= 0:
return
avg = float(position.AveragePrice)
if self._entry_price == avg and self._tracked_position * volume > 0:
self._tracked_position = volume
return
self._entry_price = self._best_price = avg
self._trail_price = None
self._tracked_position = volume
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
new_day = not self._has_previous or candle.OpenTime.Date != self._previous_time.Date
if new_day:
self._daily_open = float(candle.OpenPrice)
price = float(candle.ClosePrice)
step = float(self.Security.PriceStep or 1)
if step <= 0:
step = 1
can_order = self.IsFormedAndOnlineAndAllowTrading() and (
self._pending is None or self._pending.State in (OrderStates.Done, OrderStates.Failed))
if can_order:
pos = float(self.Position)
if pos != 0 and self._entry_price is not None and float(self._trailing_stop.Value) > 0:
entry = self._entry_price
is_long = pos > 0
best = self._best_price if self._best_price is not None else entry
best = max(best, price) if is_long else min(best, price)
self._best_price = best
if (best - entry if is_long else entry - best) >= float(self._trailing_start.Value) * step:
candidate = best - float(self._trailing_stop.Value) * step if is_long else best + float(self._trailing_stop.Value) * step
current = self._trail_price
movement = float(self._trailing_step.Value) * step
if current is None or (candidate >= current + movement if is_long else candidate <= current - movement):
self._trail_price = candidate
if self._trail_price is not None and (price <= self._trail_price if is_long else price >= self._trail_price):
self._pending = self.SellMarket(abs(self.Position)) if is_long else self.BuyMarket(abs(self.Position))
elif pos == 0 and not new_day and self._has_previous and self._previous_time.Date == candle.OpenTime.Date:
body = abs(self._previous_close - self._previous_open)
offset = float(self._break_point.Value) * step
buy_level = self._daily_open + offset
sell_level = self._daily_open - offset
valid_size = float(self._last_bar_min.Value) * step <= body <= float(self._last_bar_max.Value) * step
if valid_size and self._previous_close > self._previous_open and price > buy_level and self._previous_open <= buy_level <= self._previous_close:
self._pending = self.BuyMarket(self.Volume)
elif valid_size and self._previous_close < self._previous_open and price < sell_level and self._previous_close <= sell_level <= self._previous_open:
self._pending = self.SellMarket(self.Volume)
self._previous_open = float(candle.OpenPrice)
self._previous_close = price
self._previous_time = candle.OpenTime
self._has_previous = True
def CreateClone(self):
return daily_breakpoint_strategy()