Bezier ReOpen-Strategie
Die Bezier ReOpen-Strategie verwendet einen benutzerdefinierten Bezier-Kurven-Indikator, um der Trendrichtung zu folgen. Wenn der Indikator nach oben dreht und der letzte Wert über dem vorherigen liegt, kann die Strategie eine Long-Position eröffnen. Wenn der Indikator nach unten dreht, kann sie eine Short-Position eröffnen. Bestehende Positionen werden geschlossen, wenn der Indikator die Richtung wechselt. Nach dem Einstieg werden zusätzliche Positionen jedes Mal neu eröffnet, wenn der Preis um einen benutzerdefinierten Schritt fortschreitet, was ein Skalieren in den Trend ermöglicht.
Diese Implementierung basiert auf dem MetaTrader Expert Advisor Exp_Bezier_ReOpen.mq5 (ID 16883).
Details
- Indikator: Bezier-Kurve aus den letzten
BPeriodPreisen und ParameterTfür die Kurvenspannung. - Einstieg:
- Long: Indikatorsteigung dreht nach oben und aktueller Wert liegt über dem vorherigen.
- Short: Indikatorsteigung dreht nach unten und aktueller Wert liegt unter dem vorherigen.
- Ausstieg:
- Long: Indikatorsteigung dreht nach unten.
- Short: Indikatorsteigung dreht nach oben.
- Wiedereinstieg: nach dem ersten Einstieg wird jedes Mal eine zusätzliche Order gesendet, wenn sich der Preis
PriceStepvom letzten Einstiegspreis entfernt, bis zuPosTotalOrders. - Stops: optionaler Stop-Loss und Take-Profit in absoluten Preiseinheiten.
Parameter
CandleType– Kerzen-Zeitrahmen für Berechnungen. Standard: 4 Stunden.BPeriod– Anzahl der Balken für die Bezier-Berechnung. Standard: 8.T– Bezier-Kurvenspannung (0..1). Standard: 0.5.PriceType– Preisquelle für den Indikator (close, open, high, low, median, typical, weighted). Standard: weighted.PriceStep– Preisabstand zum Senden zusätzlicher Orders. Standard: 300.PosTotal– maximale Anzahl von Positionen in der Skalierungssequenz. Standard: 10.BuyPosOpen– Long-Positionen öffnen erlauben. Standard: true.SellPosOpen– Short-Positionen öffnen erlauben. Standard: true.BuyPosClose– Longs bei entgegengesetztem Signal schließen. Standard: true.SellPosClose– Shorts bei entgegengesetztem Signal schließen. Standard: true.StopLoss– Stop-Loss in Preiseinheiten. Standard: 1000.TakeProfit– Take-Profit in Preiseinheiten. Standard: 2000.
Filter-Tags
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Benutzerdefiniert
- Stops: Optional
- Komplexität: Moderat
- Zeitrahmen: Mittelfristig
- Risikolevel: Moderat
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>
/// Bezier re-open strategy based on a custom Bezier curve calculation.
/// Opens positions when the Bezier value changes direction and re-enters
/// after price moves by a specified step.
/// </summary>
public class BezierReOpenStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bPeriod;
private readonly StrategyParam<decimal> _t;
private readonly StrategyParam<decimal> _priceStep;
private readonly StrategyParam<int> _posTotal;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _buyPosClose;
private readonly StrategyParam<bool> _sellPosClose;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly List<decimal> _prices = new();
private decimal _prev1;
private decimal _prev2;
private int _orderCount;
private decimal _lastEntryPrice;
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of bars for Bezier calculation.
/// </summary>
public int BPeriod
{
get => _bPeriod.Value;
set => _bPeriod.Value = value;
}
/// <summary>
/// Bezier curve tension.
/// </summary>
public decimal T
{
get => _t.Value;
set => _t.Value = value;
}
/// <summary>
/// Price distance for additional entries.
/// </summary>
public decimal PriceStep
{
get => _priceStep.Value;
set => _priceStep.Value = value;
}
/// <summary>
/// Maximum number of positions in sequence.
/// </summary>
public int PosTotal
{
get => _posTotal.Value;
set => _posTotal.Value = value;
}
/// <summary>
/// Allow long entries.
/// </summary>
public bool BuyPosOpen
{
get => _buyPosOpen.Value;
set => _buyPosOpen.Value = value;
}
/// <summary>
/// Allow short entries.
/// </summary>
public bool SellPosOpen
{
get => _sellPosOpen.Value;
set => _sellPosOpen.Value = value;
}
/// <summary>
/// Close longs on opposite signal.
/// </summary>
public bool BuyPosClose
{
get => _buyPosClose.Value;
set => _buyPosClose.Value = value;
}
/// <summary>
/// Close shorts on opposite signal.
/// </summary>
public bool SellPosClose
{
get => _sellPosClose.Value;
set => _sellPosClose.Value = value;
}
/// <summary>
/// Stop-loss in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take-profit in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BezierReOpenStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_bPeriod = Param(nameof(BPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Bezier Period", "Number of bars for Bezier calculation", "Indicator");
_t = Param(nameof(T), 0.5m)
.SetDisplay("T", "Bezier curve tension", "Indicator");
_priceStep = Param(nameof(PriceStep), 300m)
.SetGreaterThanZero()
.SetDisplay("Price Step", "Price distance for additional entries", "Trading");
_posTotal = Param(nameof(PosTotal), 1)
.SetGreaterThanZero()
.SetDisplay("Max Positions", "Maximum number of positions", "Trading");
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Enabled", "Allow long entries", "Trading");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Enabled", "Allow short entries", "Trading");
_buyPosClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Close Long", "Close longs on opposite signal", "Trading");
_sellPosClose = Param(nameof(SellPosClose), true)
.SetDisplay("Close Short", "Close shorts on opposite signal", "Trading");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop-loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take-profit in price units", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prices.Clear();
_prev1 = 0m;
_prev2 = 0m;
_orderCount = 0;
_lastEntryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private decimal ComputeBezier()
{
var n = BPeriod;
if (_prices.Count < n + 1)
return 0m;
var t = (double)T;
double result = 0;
for (var i = 0; i <= n; i++)
{
var priceVal = (double)_prices[_prices.Count - n - 1 + i];
result += priceVal * Binomial(n, i) * Math.Pow(t, i) * Math.Pow(1 - t, n - i);
}
return (decimal)result;
}
private static double Binomial(int n, int k)
{
return Factorial(n) / (Factorial(k) * Factorial(n - k));
}
private static double Factorial(int value)
{
var res = 1d;
for (var j = 2; j <= value; j++)
res *= j;
return res;
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var price = (2m * candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 4m;
_prices.Add(price);
if (_prices.Count > BPeriod + 2)
_prices.RemoveAt(0);
var bezierValue = ComputeBezier();
if (bezierValue == 0m)
return;
_prev2 = _prev1;
_prev1 = bezierValue;
if (_prev2 == 0m)
return;
var openLong = false;
var openShort = false;
var closeLong = false;
var closeShort = false;
if (_prev1 > _prev2)
{
if (BuyPosOpen)
openLong = true;
if (SellPosClose && Position < 0)
closeShort = true;
}
else if (_prev1 < _prev2)
{
if (SellPosOpen)
openShort = true;
if (BuyPosClose && Position > 0)
closeLong = true;
}
if (closeLong)
SellMarket();
if (closeShort)
BuyMarket();
if (openLong && Position <= 0)
{
BuyMarket();
_orderCount = 1;
_lastEntryPrice = candle.ClosePrice;
return;
}
if (openShort && Position >= 0)
{
SellMarket();
_orderCount = 1;
_lastEntryPrice = candle.ClosePrice;
return;
}
if (Position > 0 && _orderCount < PosTotal)
{
if (candle.ClosePrice - _lastEntryPrice >= PriceStep)
{
BuyMarket();
_lastEntryPrice = candle.ClosePrice;
_orderCount++;
}
}
else if (Position < 0 && _orderCount < PosTotal)
{
if (_lastEntryPrice - candle.ClosePrice >= PriceStep)
{
SellMarket();
_lastEntryPrice = candle.ClosePrice;
_orderCount++;
}
}
CheckStops(candle.ClosePrice);
}
private void CheckStops(decimal price)
{
if (Position > 0)
{
if (StopLoss > 0 && price <= _lastEntryPrice - StopLoss)
SellMarket();
if (TakeProfit > 0 && price >= _lastEntryPrice + TakeProfit)
SellMarket();
}
else if (Position < 0)
{
if (StopLoss > 0 && price >= _lastEntryPrice + StopLoss)
BuyMarket();
if (TakeProfit > 0 && price <= _lastEntryPrice - TakeProfit)
BuyMarket();
}
}
}
import clr
import math
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 bezier_reopen_strategy(Strategy):
def __init__(self):
super(bezier_reopen_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._b_period = self.Param("BPeriod", 8) \
.SetDisplay("Bezier Period", "Number of bars for Bezier calculation", "Indicator")
self._t = self.Param("T", 0.5) \
.SetDisplay("T", "Bezier curve tension", "Indicator")
self._price_step_param = self.Param("PriceStep", 300.0) \
.SetDisplay("Price Step", "Price distance for additional entries", "Trading")
self._pos_total = self.Param("PosTotal", 1) \
.SetDisplay("Max Positions", "Maximum number of positions", "Trading")
self._buy_pos_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Buy Enabled", "Allow long entries", "Trading")
self._sell_pos_open = self.Param("SellPosOpen", True) \
.SetDisplay("Sell Enabled", "Allow short entries", "Trading")
self._buy_pos_close = self.Param("BuyPosClose", True) \
.SetDisplay("Close Long", "Close longs on opposite signal", "Trading")
self._sell_pos_close = self.Param("SellPosClose", True) \
.SetDisplay("Close Short", "Close shorts on opposite signal", "Trading")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop-loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take-profit in price units", "Risk")
self._prices = []
self._prev1 = 0.0
self._prev2 = 0.0
self._order_count = 0
self._last_entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@property
def b_period(self):
return self._b_period.Value
@property
def t_param(self):
return self._t.Value
@property
def price_step_param(self):
return self._price_step_param.Value
@property
def pos_total(self):
return self._pos_total.Value
@property
def buy_pos_open(self):
return self._buy_pos_open.Value
@property
def sell_pos_open(self):
return self._sell_pos_open.Value
@property
def buy_pos_close(self):
return self._buy_pos_close.Value
@property
def sell_pos_close(self):
return self._sell_pos_close.Value
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def take_profit(self):
return self._take_profit.Value
def OnReseted(self):
super(bezier_reopen_strategy, self).OnReseted()
self._prices = []
self._prev1 = 0.0
self._prev2 = 0.0
self._order_count = 0
self._last_entry_price = 0.0
def OnStarted2(self, time):
super(bezier_reopen_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _factorial(self, value):
res = 1.0
for j in range(2, value + 1):
res *= j
return res
def _binomial(self, n, k):
return self._factorial(n) / (self._factorial(k) * self._factorial(n - k))
def _compute_bezier(self):
n = self.b_period
if len(self._prices) < n + 1:
return 0.0
t = self.t_param
result = 0.0
for i in range(n + 1):
price_val = self._prices[len(self._prices) - n - 1 + i]
result += price_val * self._binomial(n, i) * (t ** i) * ((1 - t) ** (n - i))
return result
def _check_stops(self, price):
if self.Position > 0:
if self.stop_loss > 0 and price <= self._last_entry_price - self.stop_loss:
self.SellMarket()
if self.take_profit > 0 and price >= self._last_entry_price + self.take_profit:
self.SellMarket()
elif self.Position < 0:
if self.stop_loss > 0 and price >= self._last_entry_price + self.stop_loss:
self.BuyMarket()
if self.take_profit > 0 and price <= self._last_entry_price - self.take_profit:
self.BuyMarket()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
price = (2.0 * float(candle.ClosePrice) + float(candle.HighPrice) + float(candle.LowPrice)) / 4.0
self._prices.append(price)
if len(self._prices) > self.b_period + 2:
self._prices.pop(0)
bezier_value = self._compute_bezier()
if bezier_value == 0.0:
return
self._prev2 = self._prev1
self._prev1 = bezier_value
if self._prev2 == 0.0:
return
open_long = False
open_short = False
close_long = False
close_short = False
if self._prev1 > self._prev2:
if self.buy_pos_open:
open_long = True
if self.sell_pos_close and self.Position < 0:
close_short = True
elif self._prev1 < self._prev2:
if self.sell_pos_open:
open_short = True
if self.buy_pos_close and self.Position > 0:
close_long = True
if close_long:
self.SellMarket()
if close_short:
self.BuyMarket()
if open_long and self.Position <= 0:
self.BuyMarket()
self._order_count = 1
self._last_entry_price = float(candle.ClosePrice)
return
if open_short and self.Position >= 0:
self.SellMarket()
self._order_count = 1
self._last_entry_price = float(candle.ClosePrice)
return
if self.Position > 0 and self._order_count < self.pos_total:
if float(candle.ClosePrice) - self._last_entry_price >= self.price_step_param:
self.BuyMarket()
self._last_entry_price = float(candle.ClosePrice)
self._order_count += 1
elif self.Position < 0 and self._order_count < self.pos_total:
if self._last_entry_price - float(candle.ClosePrice) >= self.price_step_param:
self.SellMarket()
self._last_entry_price = float(candle.ClosePrice)
self._order_count += 1
self._check_stops(float(candle.ClosePrice))
def CreateClone(self):
return bezier_reopen_strategy()