Diese Strategie ist eine StockSharp-Portierung des MetaTrader-Expertenberaters "DeMarker gewinnt Positionsvolumen". Es verwendet den DeMarker-Oszillator, um überverkaufte und überkaufte Extremwerte zu erkennen und das Risiko schrittweise zu erhöhen, wenn der Markt in einem angespannten Zustand bleibt. Die Implementierung arbeitet mit abgeschlossenen Kerzen und stellt sicher, dass nur ein Signal pro Balken verarbeitet wird.
Die C#-Version konzentriert sich auf die zentrale diskretionäre Logik des ursprünglichen Skripts und übernimmt gleichzeitig die übergeordnete StockSharp API. Ordermanagement, Volumenwachstum und optionales Umkehrverhalten sind über Strategieparameter verfügbar, sodass der Algorithmus an unterschiedliche Märkte und Zeitrahmen angepasst werden kann.
Parameter
DeMarker-Zeitraum – Anzahl der vom DeMarker-Indikator verwendeten Kerzen.
Upper Level – Oszillatorschwelle, die eine kurze Belichtung vorbereitet (Standard 0.7).
Unterer Pegel – Oszillatorschwelle, die Langzeitbelichtung vorbereitet (Standard 0.3).
Handelsvolumen – Marktauftragsvolumen, das bei jedem Signal übermittelt wird.
Nur eine Position – wenn diese Option aktiviert ist, wird die Strategie vor der Eröffnung eines neuen Handels abgeflacht, sodass das Nettoengagement niemals Long- und Short-Positionen vermischt.
Umgekehrte Signale – tauscht Kauf- und Verkaufsauslöser aus und verwandelt die Strategie in eine konträre oder trendfolgende Version.
Kerzentyp – Zeitrahmen der Kerzen, die für die Indikator- und Signalbewertung verwendet werden.
Handelslogik
Für den ausgewählten Zeitraum wird ein Kerzenabonnement eröffnet und in einen DeMarker-Indikator eingespeist.
Wenn die letzte fertige Kerze schließt, wird der aktuelle DeMarker-Wert mit den konfigurierten Werten verglichen.
Ohne Umkehrung:
Wenn DeMarker unter dem unteren Niveau liegt, versucht die Strategie, eine Long-Position aufzubauen oder auszubauen.
Wenn DeMarker über dem oberen Niveau liegt, versucht die Strategie, eine Short-Position aufzubauen oder auszubauen.
Wenn die Umkehrung aktiviert ist, wird die Bedeutung der Niveaus umgekehrt (extreme Tiefs lösen Short-Positionen aus und extreme Hochs lösen Long-Positionen aus).
Der Algorithmus merkt sich die Balkenzeit des zuletzt ausgeführten Handels, um mehrere Einträge bei derselben Kerze zu vermeiden.
Positionsmanagement
Vor dem Richtungsumkehr prüft die Strategie den nicht realisierten Gewinn der bestehenden Position. Das entgegengesetzte Engagement wird nur geschlossen, wenn der aktuelle Kerzenpreis den Handel mit einem positiven Ergebnis verlässt, was das Schutzverhalten des ursprünglichen EA widerspiegelt.
Positionsdurchschnitte werden intern verfolgt. Wenn weitere Aufträge in die gleiche Richtung hinzugefügt werden, wird der Durchschnittspreis neu berechnet, um die Rentabilität korrekt zu bewerten.
Der optionale Parameter Only One Position erzwingt einen Flat-Status vor der Eingabe eines neuen Handels, was hilfreich ist, wenn Sie im Nettopositionsmodus arbeiten.
StartProtection() wird aufgerufen, sobald die Strategie beginnt, um sicherzustellen, dass eine Notfallliquidation verfügbar bleibt, wenn die Position ungleich Null wird und der Algorithmus stoppt.
Notizen
Die Konvertierung ist für die übergeordnete Ebene StockSharp API konzipiert und basiert nicht auf benutzerdefinierten Sammlungen oder direkten Abfragen von Indikatorwerten.
Risikogrößenmodelle aus der Version MetaTrader (feste Marge, prozentuales Risiko usw.) werden bewusst auf den konstanten Parameter Trade Volume vereinfacht. Passen Sie die Positionsgröße extern an, wenn eine dynamische Risikokontrolle erforderlich ist.
Da Ausführungen mit Marktaufträgen zu Kerzenschlusskursen simuliert werden, denken Sie daran, die Konfiguration anhand der tatsächlichen Broker-Ausführungs- und Slippage-Anforderungen zu validieren.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Uses the DeMarker oscillator to accumulate positions when extreme levels are reached.
/// </summary>
public class DeMarkerGainingPositionVolumeStrategy : Strategy
{
private readonly StrategyParam<int> _deMarkerPeriod;
private readonly StrategyParam<decimal> _upperLevel;
private readonly StrategyParam<decimal> _lowerLevel;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private decimal? _prevOscillator;
/// <summary>
/// Number of candles used by the DeMarker indicator.
/// </summary>
public int DeMarkerPeriod
{
get => _deMarkerPeriod.Value;
set => _deMarkerPeriod.Value = value;
}
/// <summary>
/// DeMarker level that triggers short entries.
/// </summary>
public decimal UpperLevel
{
get => _upperLevel.Value;
set => _upperLevel.Value = value;
}
/// <summary>
/// DeMarker level that triggers long entries.
/// </summary>
public decimal LowerLevel
{
get => _lowerLevel.Value;
set => _lowerLevel.Value = value;
}
/// <summary>
/// Candle type that defines the timeframe for the oscillator.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public DeMarkerGainingPositionVolumeStrategy()
{
_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 14)
.SetDisplay("DeMarker Period", "Number of bars used by the oscillator.", "Indicator");
_upperLevel = Param(nameof(UpperLevel), 0.7m)
.SetDisplay("Upper Level", "Threshold that prepares short exposure.", "Indicator");
_lowerLevel = Param(nameof(LowerLevel), 0.3m)
.SetDisplay("Lower Level", "Threshold that prepares long exposure.", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for DeMarker calculations.", "Data");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevOscillator = null;
_rsi = new RelativeStrengthIndex { Length = DeMarkerPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed)
{
_prevOscillator = rsiValue / 100m;
return;
}
var oscillatorValue = rsiValue / 100m;
if (_prevOscillator is null)
{
_prevOscillator = oscillatorValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Oscillator crosses below the lower level => oversold => buy
if (_prevOscillator > LowerLevel && oscillatorValue <= LowerLevel)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
// Oscillator crosses above the upper level => overbought => sell
else if (_prevOscillator < UpperLevel && oscillatorValue >= UpperLevel)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevOscillator = oscillatorValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_rsi = null;
_prevOscillator = null;
base.OnReseted();
}
}