Täglicher Ausbruch-Strategie
Diese Strategie handelt Ausbrüche vom täglichen Eröffnungskurs. Zu Beginn eines jeden neuen Tages wird der Eröffnungskurs gespeichert. Wenn der Preis sich von diesem Level um eine benutzerdefinierte Anzahl von Punkten entfernt und der vorherige Balken innerhalb eines konfigurierbaren Größenbereichs liegt, tritt die Strategie in die Ausbruchsrichtung ein.
Einstiegslogik
- Wenn der vorherige Balken bullisch ist und der Preis um Break Point Punkte über die Tageseröffnung steigt, wird eine Long-Position eröffnet.
- Wenn der vorherige Balken bärisch ist und der Preis um Break Point Punkte unter die Tageseröffnung fällt, wird eine Short-Position eröffnet.
- Die Größe des vorherigen Balkens muss zwischen Last Bar Min und Last Bar Max Punkten liegen.
- Das Ausbruchsniveau muss innerhalb des Körpers des vorherigen Balkens liegen.
Risikomanagement
- Optionaler Take Profit und Stop Loss werden in Punkten vom Einstiegspreis gemessen.
- Ein Trailing Stop kann mit den Parametern Trailing Start, Trailing Stop und Trailing Step aktiviert werden. Wenn der Preis sich um Trailing Start Punkte zugunsten bewegt, wird der Stop auf Trailing Stop Punkte vom Einstieg gesetzt und folgt dann in Trailing Step-Schritten.
Parameter
| Name | Beschreibung |
|---|---|
| Candle Type | Zeitrahmen der verarbeiteten Kerzen. |
| Break Point | Abstand von der Tageseröffnung zum Auslösen eines Trades (Punkte). |
| Last Bar Min | Mindestgröße des vorherigen Balkens (Punkte). |
| Last Bar Max | Maximale Größe des vorherigen Balkens (Punkte). |
| Trailing Start | Preisbewegung zum Starten des Trailing Stops (Punkte). |
| Trailing Stop | Anfänglicher Trailing-Abstand (Punkte). |
| Trailing Step | Schritt zum Bewegen des Trailing Stops (Punkte). |
| Take Profit | Take-Profit-Abstand (Punkte). |
| Stop Loss | Stop-Loss-Abstand (Punkte). |
Hinweise
Die Strategie arbeitet nur auf abgeschlossenen Kerzen und verwendet Marktaufträge für Ein- und Ausstiege. Sie speichert interne Variablen für die Daten des vorherigen Balkens und das Trailing-Stop-Level.
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()