Die Kleine Inside-Bar-Strategie sucht nach einem kompakten Inside-Bar-Muster, dem ein Momentum-Wechsel zwischen zwei aufeinanderfolgenden Kerzen folgt. Der ursprüngliche MetaTrader 5-Experte wurde in die StockSharp High-Level-API übersetzt und arbeitet jetzt ausschließlich auf abgeschlossenen Kerzen. Der Ansatz richtet sich an Trader, die Ausbruchs-Einstiege bevorzugen, die durch komprimierte Volatilitätsphasen ausgelöst werden.
Musterdefinition
Die Strategie bewertet die zwei jüngsten abgeschlossenen Kerzen:
Inside-Bar-Bedingung – die zuletzt abgeschlossene Kerze muss vollständig im Bereich der vorherigen liegen.
Range-Ratio-Filter – der Bereich der Mutterbar (zwei Bars zurück) muss mindestens einem konfigurierbaren Vielfachen des Inside-Bar-Bereichs entsprechen. Das Standard-Verhältnis ist 2:1.
Richtungsfilter –
Ein Long-Setup erfordert eine bullische Inside-Bar, die sich in der unteren Hälfte der Mutterbar bildet, zusammen mit einer bärischen Mutterbar.
Ein Short-Setup erfordert eine bärische Inside-Bar, die sich in der oberen Hälfte der Mutterbar bildet, zusammen mit einer bullischen Mutterbar.
Optionale Umkehr tauscht die Long- und Short-Interpretationen, behält aber die gleichen geometrischen Anforderungen bei.
Positionsverwaltung
Der Parameter OpenMode spiegelt das Verhalten des ursprünglichen EA wider:
AnySignal – übermittelt bei jedem Signal eine neue Market-Order. Wenn eine entgegengesetzte Position vorhanden ist, wird sie teilweise ausgeglichen, da StockSharp Netting-Konten verwendet.
SwingWithRefill – flacht die entgegengesetzte Exposition vor dem Einstieg ab und erlaubt mehrere Ergänzungen in dieselbe Richtung.
SingleSwing – hält höchstens einen direktionalen Trade; neue Signale werden ignoriert, solange eine ausgerichtete Position offen ist.
Sowohl Long- als auch Short-Einstiege können unabhängig aktiviert werden. Reversal-Trading invertiert einfach, welches Setup Long- oder Short-Orders produziert.
Parameter
Name
Standard
Beschreibung
CandleType
1-Stunden-Zeitrahmen
Kerzen-Subscription zur Mustererkennung.
RangeRatioThreshold
2.0
Minimales Mutter-zu-Inside-Range-Verhältnis.
EnableLong
true
Bullische Trades erlauben.
EnableShort
true
Bärische Trades erlauben.
ReverseSignals
false
Long- und Short-Musterrichtungen tauschen.
OpenMode
SwingWithRefill
Steuert, wie bestehende Exposition bei einem neuen Signal behandelt wird.
Handelslogik
Die konfigurierte Kerzenserie abonnieren und auf abgeschlossene Bars warten.
Die letzten zwei abgeschlossenen Kerzen zur Musterbewertung pflegen.
Wenn Muster und Ratio-Filter übereinstimmen, das direktionale Signal bestimmen und optional Umkehr anwenden.
Bestätigen, dass der Handel erlaubt ist (IsFormedAndOnlineAndAllowTrading) und dass die relevante Richtung aktiviert ist.
Ordergröße basierend auf dem ausgewählten OpenMode berechnen und eine Market-Order mit dem Basis-Strategie-Volumen senden.
Die interne Kerzenhistorie aktualisieren, damit die neueste Kerze Teil des nächsten Bewertungszyklus wird.
Implementierungshinweise
Die Strategie verwendet StartProtection(), um den eingebauten Risikomanager zu aktivieren (ohne vordefinierte Stop- oder Take-Profit-Werte). Zusätzliche Filter können bei Bedarf extern hinzugefügt werden.
Der Indikatorzustand wird nicht in Sammlungen gespeichert; es werden nur die zwei neuesten Kerzen wie für das Muster erforderlich gehalten.
Der Algorithmus stützt sich ausschließlich auf abgeschlossene Kerzen und vermeidet Intrabar-Berechnungen gemäß den Best Practices der High-Level-API.
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>
/// Implements the "Small Inside Bar" pattern strategy converted from MetaTrader 5.
/// The strategy searches for an inside bar with a small range compared to the mother bar
/// and opens positions following the direction of the pattern conditions.
/// </summary>
public class SmallInsideBarStrategy : Strategy
{
/// <summary>
/// Defines how the strategy manages simultaneous entries.
/// </summary>
public enum SmallInsideBarOpenModes
{
/// <summary>
/// Open a new position on every signal without forcing opposite positions to close.
/// </summary>
AnySignal,
/// <summary>
/// Close opposite positions first and allow adding to the current swing direction.
/// </summary>
SwingWithRefill,
/// <summary>
/// Maintain a single position in the market and ignore additional entries while it is active.
/// </summary>
SingleSwing
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _rangeRatioThreshold;
private readonly StrategyParam<bool> _enableLong;
private readonly StrategyParam<bool> _enableShort;
private readonly StrategyParam<bool> _reverseSignals;
private readonly StrategyParam<SmallInsideBarOpenModes> _openMode;
private ICandleMessage _previousCandle;
private ICandleMessage _twoBackCandle;
/// <summary>
/// Type of candles used for pattern detection.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Minimum ratio between the mother bar range and the inside bar range.
/// </summary>
public decimal RangeRatioThreshold
{
get => _rangeRatioThreshold.Value;
set => _rangeRatioThreshold.Value = value;
}
/// <summary>
/// Allow long trades.
/// </summary>
public bool EnableLong
{
get => _enableLong.Value;
set => _enableLong.Value = value;
}
/// <summary>
/// Allow short trades.
/// </summary>
public bool EnableShort
{
get => _enableShort.Value;
set => _enableShort.Value = value;
}
/// <summary>
/// Reverse long and short signals.
/// </summary>
public bool ReverseSignals
{
get => _reverseSignals.Value;
set => _reverseSignals.Value = value;
}
/// <summary>
/// Mode for handling position entries.
/// </summary>
public SmallInsideBarOpenModes OpenMode
{
get => _openMode.Value;
set => _openMode.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public SmallInsideBarStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time frame used for pattern detection", "General");
_rangeRatioThreshold = Param(nameof(RangeRatioThreshold), 2.25m)
.SetGreaterThanZero()
.SetDisplay("Range Ratio", "Minimum mother-to-inside bar range ratio", "Pattern")
.SetOptimize(1.5m, 3m, 0.25m);
_enableLong = Param(nameof(EnableLong), true)
.SetDisplay("Enable Long", "Allow bullish trades", "Trading");
_enableShort = Param(nameof(EnableShort), true)
.SetDisplay("Enable Short", "Allow bearish trades", "Trading");
_reverseSignals = Param(nameof(ReverseSignals), false)
.SetDisplay("Reverse Signals", "Invert long and short signals", "Trading");
_openMode = Param(nameof(OpenMode), SmallInsideBarOpenModes.SwingWithRefill)
.SetDisplay("Open Mode", "Position management mode", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousCandle = null;
_twoBackCandle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_previousCandle == null)
{
_previousCandle = candle;
return;
}
if (_twoBackCandle == null)
{
_twoBackCandle = _previousCandle;
_previousCandle = candle;
return;
}
var insideHigh = _previousCandle.HighPrice;
var insideLow = _previousCandle.LowPrice;
var motherHigh = _twoBackCandle.HighPrice;
var motherLow = _twoBackCandle.LowPrice;
if (insideHigh <= insideLow || motherHigh <= motherLow)
{
ShiftHistory(candle);
return;
}
if (!(insideHigh < motherHigh && insideLow > motherLow))
{
ShiftHistory(candle);
return;
}
var insideRange = insideHigh - insideLow;
var motherRange = motherHigh - motherLow;
var ratio = insideRange == 0 ? decimal.MaxValue : motherRange / insideRange;
if (ratio <= RangeRatioThreshold)
{
ShiftHistory(candle);
return;
}
var midpoint = (motherHigh + motherLow) / 2m;
var bullishInside = _previousCandle.ClosePrice > _previousCandle.OpenPrice && insideHigh < midpoint && _twoBackCandle.ClosePrice < _twoBackCandle.OpenPrice;
var bearishInside = _previousCandle.ClosePrice < _previousCandle.OpenPrice && insideLow < midpoint && _twoBackCandle.ClosePrice > _twoBackCandle.OpenPrice;
if (ReverseSignals)
{
(bullishInside, bearishInside) = (bearishInside, bullishInside);
}
var shouldOpenLong = bullishInside && EnableLong;
var shouldOpenShort = bearishInside && EnableShort;
if (shouldOpenLong)
{
var volume = CalculateOrderVolume(true);
if (volume > 0)
BuyMarket(volume);
}
if (shouldOpenShort)
{
var volume = CalculateOrderVolume(false);
if (volume > 0)
SellMarket(volume);
}
ShiftHistory(candle);
}
private decimal CalculateOrderVolume(bool isLong)
{
var baseVolume = Volume;
if (baseVolume <= 0)
return 0;
var position = Position;
if (isLong)
{
if (OpenMode == SmallInsideBarOpenModes.SingleSwing && position > 0)
return 0;
if (position < 0 && OpenMode != SmallInsideBarOpenModes.AnySignal)
baseVolume += Math.Abs(position);
}
else
{
if (OpenMode == SmallInsideBarOpenModes.SingleSwing && position < 0)
return 0;
if (position > 0 && OpenMode != SmallInsideBarOpenModes.AnySignal)
baseVolume += Math.Abs(position);
}
return baseVolume;
}
private void ShiftHistory(ICandleMessage candle)
{
// Keep track of the last two finished candles for pattern evaluation.
_twoBackCandle = _previousCandle;
_previousCandle = candle;
}
}
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 small_inside_bar_strategy(Strategy):
def __init__(self):
super(small_inside_bar_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._range_ratio_threshold = self.Param("RangeRatioThreshold", 2.25)
self._enable_long = self.Param("EnableLong", True)
self._enable_short = self.Param("EnableShort", True)
self._reverse_signals = self.Param("ReverseSignals", False)
self._prev_candle_high = None
self._prev_candle_low = None
self._prev_candle_open = None
self._prev_candle_close = None
self._two_back_high = None
self._two_back_low = None
self._two_back_open = None
self._two_back_close = None
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(small_inside_bar_strategy, self).OnStarted2(time)
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._prev_candle_high is None:
self._cache_prev(candle)
return
if self._two_back_high is None:
self._shift_history(candle)
return
inside_high = self._prev_candle_high
inside_low = self._prev_candle_low
mother_high = self._two_back_high
mother_low = self._two_back_low
if inside_high <= inside_low or mother_high <= mother_low:
self._shift_history(candle)
return
if not (inside_high < mother_high and inside_low > mother_low):
self._shift_history(candle)
return
inside_range = inside_high - inside_low
mother_range = mother_high - mother_low
ratio = mother_range / inside_range if inside_range != 0 else 1e18
if ratio <= self._range_ratio_threshold.Value:
self._shift_history(candle)
return
midpoint = (mother_high + mother_low) / 2.0
bullish_inside = (self._prev_candle_close > self._prev_candle_open and
inside_high < midpoint and
self._two_back_close < self._two_back_open)
bearish_inside = (self._prev_candle_close < self._prev_candle_open and
inside_low < midpoint and
self._two_back_close > self._two_back_open)
if self._reverse_signals.Value:
bullish_inside, bearish_inside = bearish_inside, bullish_inside
should_open_long = bullish_inside and self._enable_long.Value
should_open_short = bearish_inside and self._enable_short.Value
if should_open_long:
volume = self._calc_order_volume(True)
if volume > 0:
self.BuyMarket(volume)
if should_open_short:
volume = self._calc_order_volume(False)
if volume > 0:
self.SellMarket(volume)
self._shift_history(candle)
def _calc_order_volume(self, is_long):
base_volume = float(self.Volume)
if base_volume <= 0:
return 0
position = self.Position
if is_long:
if position < 0:
base_volume += abs(position)
else:
if position > 0:
base_volume += abs(position)
return base_volume
def _cache_prev(self, candle):
self._prev_candle_high = float(candle.HighPrice)
self._prev_candle_low = float(candle.LowPrice)
self._prev_candle_open = float(candle.OpenPrice)
self._prev_candle_close = float(candle.ClosePrice)
def _shift_history(self, candle):
self._two_back_high = self._prev_candle_high
self._two_back_low = self._prev_candle_low
self._two_back_open = self._prev_candle_open
self._two_back_close = self._prev_candle_close
self._cache_prev(candle)
def OnReseted(self):
super(small_inside_bar_strategy, self).OnReseted()
self._prev_candle_high = None
self._prev_candle_low = None
self._prev_candle_open = None
self._prev_candle_close = None
self._two_back_high = None
self._two_back_low = None
self._two_back_open = None
self._two_back_close = None
def CreateClone(self):
return small_inside_bar_strategy()