Exp-Extremum-Strategie
Diese Strategie handelt Umkehrungen, die durch den Vergleich von Preisextremen über ein Rückblickfenster erkannt werden. Sie beobachtet, ob die aktuelle Kerze den Preis über vorherige Hochs oder Tiefs treibt, und reagiert, wenn sich das Vorzeichen dieses Vergleichs ändert.
Funktionsweise
- Für jede abgeschlossene Kerze ermittelt die Strategie:
- Das niedrigste Hoch über die letzten N Bars.
- Das höchste Tief über die letzten N Bars.
- Die Differenzen zwischen dem aktuellen Hoch/Tief und diesen Niveaus werden summiert.
- Eine positive Summe zeigt Aufwärtsdruck, eine negative Summe zeigt Abwärtsdruck an.
- Wenn das Vorzeichen von vor zwei Bars dem Vorzeichen vom letzten Bar entgegengesetzt ist, erscheint ein Umkehrsignal:
- Aufwärts dann Abwärts → Long-Position eröffnen.
- Abwärts dann Aufwärts → Short-Position eröffnen.
- Optionale Berechtigungen erlauben das unabhängige Deaktivieren des Öffnens oder Schließens von Long-/Short-Positionen.
Parameter
Length– Indikatorperiode für Extremberechnungen.CandleType– Zeitrahmen der eingehenden Kerzen.BuyPosOpen/SellPosOpen– Berechtigungen zum Öffnen von Long- oder Short-Positionen.BuyPosClose/SellPosClose– Berechtigungen zum Schließen von Long- oder Short-Positionen.
Hinweise
Die Strategie verwendet die High-Level-API mit Kerzenabonnements und integrierten Highest/Lowest-Indikatoren. Positionen werden mit Market-Orders eröffnet und über ClosePosition() geschlossen, wenn das entgegengesetzte Signal erscheint.
using System;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Extremum reversal strategy using highest and lowest comparisons.
/// </summary>
public class ExpExtremumStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _buyPosClose;
private readonly StrategyParam<bool> _sellPosClose;
private readonly Lowest _minHigh = new();
private readonly Highest _maxLow = new();
private bool _upPrev1;
private bool _dnPrev1;
private bool _upPrev2;
private bool _dnPrev2;
private int _barsSinceTrade;
/// <summary>
/// Indicator period.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPosOpen
{
get => _buyPosOpen.Value;
set => _buyPosOpen.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPosOpen
{
get => _sellPosOpen.Value;
set => _sellPosOpen.Value = value;
}
/// <summary>
/// Allow closing long positions.
/// </summary>
public bool BuyPosClose
{
get => _buyPosClose.Value;
set => _buyPosClose.Value = value;
}
/// <summary>
/// Allow closing short positions.
/// </summary>
public bool SellPosClose
{
get => _sellPosClose.Value;
set => _sellPosClose.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpExtremumStrategy"/> class.
/// </summary>
public ExpExtremumStrategy()
{
_length = Param(nameof(Length), 40)
.SetGreaterThanZero()
.SetDisplay("Period", "Indicator period", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame of the Extremum indicator", "General");
_cooldownBars = Param(nameof(CooldownBars), 2)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Signals");
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Entry", "Permission to buy", "Signals");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Entry", "Permission to sell", "Signals");
_buyPosClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Close Long", "Permission to exit long positions", "Signals");
_sellPosClose = Param(nameof(SellPosClose), true)
.SetDisplay("Close Short", "Permission to exit short positions", "Signals");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_minHigh.Length = Length;
_maxLow.Length = Length;
_minHigh.Reset();
_maxLow.Reset();
_upPrev1 = false;
_dnPrev1 = false;
_upPrev2 = false;
_dnPrev2 = false;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_minHigh.Length = Length;
_maxLow.Length = Length;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
DrawCandles(area, subscription);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var minHighValue = _minHigh.Process(new DecimalIndicatorValue(_minHigh, candle.HighPrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var maxLowValue = _maxLow.Process(new DecimalIndicatorValue(_maxLow, candle.LowPrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_minHigh.IsFormed || !_maxLow.IsFormed)
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
var pressure = (candle.HighPrice - minHighValue) + (candle.LowPrice - maxLowValue);
var up = pressure > 0m;
var dn = pressure < 0m;
var bullishReversal = _dnPrev2 && _upPrev1 && up && candle.ClosePrice > candle.OpenPrice;
var bearishReversal = _upPrev2 && _dnPrev1 && dn && candle.ClosePrice < candle.OpenPrice;
if (BuyPosClose && bearishReversal && Position > 0)
{
SellMarket(Position);
_barsSinceTrade = 0;
}
if (SellPosClose && bullishReversal && Position < 0)
{
BuyMarket(-Position);
_barsSinceTrade = 0;
}
if (_barsSinceTrade >= CooldownBars)
{
if (BuyPosOpen && bullishReversal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
if (SellPosOpen && bearishReversal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
}
_upPrev2 = _upPrev1;
_dnPrev2 = _dnPrev1;
_upPrev1 = up;
_dnPrev1 = dn;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class exp_extremum_strategy(Strategy):
def __init__(self):
super(exp_extremum_strategy, self).__init__()
self._length = self.Param("Length", 40) \
.SetDisplay("Period", "Indicator period", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Time frame of the Extremum indicator", "General")
self._cooldown_bars = self.Param("CooldownBars", 2) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Signals")
self._buy_pos_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Buy Entry", "Permission to buy", "Signals")
self._sell_pos_open = self.Param("SellPosOpen", True) \
.SetDisplay("Sell Entry", "Permission to sell", "Signals")
self._buy_pos_close = self.Param("BuyPosClose", True) \
.SetDisplay("Close Long", "Permission to exit long positions", "Signals")
self._sell_pos_close = self.Param("SellPosClose", True) \
.SetDisplay("Close Short", "Permission to exit short positions", "Signals")
self._min_high = Lowest()
self._max_low = Highest()
self._up_prev1 = False
self._dn_prev1 = False
self._up_prev2 = False
self._dn_prev2 = False
self._bars_since_trade = 0
@property
def Length(self):
return self._length.Value
@Length.setter
def Length(self, value):
self._length.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def BuyPosOpen(self):
return self._buy_pos_open.Value
@BuyPosOpen.setter
def BuyPosOpen(self, value):
self._buy_pos_open.Value = value
@property
def SellPosOpen(self):
return self._sell_pos_open.Value
@SellPosOpen.setter
def SellPosOpen(self, value):
self._sell_pos_open.Value = value
@property
def BuyPosClose(self):
return self._buy_pos_close.Value
@BuyPosClose.setter
def BuyPosClose(self, value):
self._buy_pos_close.Value = value
@property
def SellPosClose(self):
return self._sell_pos_close.Value
@SellPosClose.setter
def SellPosClose(self, value):
self._sell_pos_close.Value = value
def OnStarted2(self, time):
super(exp_extremum_strategy, self).OnStarted2(time)
self._min_high.Length = self.Length
self._max_low.Length = self.Length
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
min_high_value = float(process_float(self._min_high, candle.HighPrice, candle.OpenTime, True))
max_low_value = float(process_float(self._max_low, candle.LowPrice, candle.OpenTime, True))
if not self._min_high.IsFormed or not self._max_low.IsFormed:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
pressure = (float(candle.HighPrice) - min_high_value) + (float(candle.LowPrice) - max_low_value)
up = pressure > 0.0
dn = pressure < 0.0
bullish_reversal = self._dn_prev2 and self._up_prev1 and up and candle.ClosePrice > candle.OpenPrice
bearish_reversal = self._up_prev2 and self._dn_prev1 and dn and candle.ClosePrice < candle.OpenPrice
pos = self.Position
if self.BuyPosClose and bearish_reversal and pos > 0:
self.SellMarket(pos)
self._bars_since_trade = 0
if self.SellPosClose and bullish_reversal and pos < 0:
self.BuyMarket(-pos)
self._bars_since_trade = 0
pos = self.Position
if self._bars_since_trade >= self.CooldownBars:
if self.BuyPosOpen and bullish_reversal and pos <= 0:
self.BuyMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
if self.SellPosOpen and bearish_reversal and pos >= 0:
self.SellMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
self._up_prev2 = self._up_prev1
self._dn_prev2 = self._dn_prev1
self._up_prev1 = up
self._dn_prev1 = dn
def OnReseted(self):
super(exp_extremum_strategy, self).OnReseted()
self._min_high.Length = self.Length
self._max_low.Length = self.Length
self._min_high.Reset()
self._max_low.Reset()
self._up_prev1 = False
self._dn_prev1 = False
self._up_prev2 = False
self._dn_prev2 = False
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return exp_extremum_strategy()