Benutzerdefinierter Kerzentyp
S# ermöglicht die Erweiterung der Kerzenerstellungsfunktionen durch die Möglichkeit, mit benutzerdefinierten Kerzentypen zu arbeiten. Dies ist nützlich in Fällen, in denen Sie mit Kerzen arbeiten müssen, die derzeit nicht von S# unterstützt werden. Nachfolgend wird der Prozess der Erstellung eines eigenen Kerzentyps am Beispiel von Delta-Kerzen beschrieben (Kerzen, die auf der Grundlage der Differenz zwischen Kauf- und Verkaufsvolumen gebildet werden).
Implementierung von Delta-Kerzen
Zunächst müssen Sie Ihren eigenen Kerzen-Message-Typ erstellen. Der Typ muss von der Klasse CandleMessage erben:
/// <summary> /// Kerze, die auf Basis des Deltas von Kauf- und Verkaufsvolumen gebildet wird. /// </summary> public class DeltaCandleMessage : CandleMessage { // Nachrichtentypkennung aus dem Helper abrufen // um denselben Wert in RegisterCandleType zu verwenden /// <summary> /// Neue Instanz von <see cref="DeltaCandleMessage"/> initialisieren. /// </summary> public DeltaCandleMessage() : base(DeltaCandleHelper.DeltaCandleType) { } /// <summary> /// Delta-Schwellenwert für die Kerzenbildung. /// </summary> public decimal DeltaThreshold { get; set; } /// <summary> /// Aktueller Delta-Wert. /// </summary> public decimal CurrentDelta { get; set; } /// <summary> /// Kopie von <see cref="DeltaCandleMessage"/> erstellen. /// </summary> /// <returns>Kopie.</returns> public override Message Clone() { return CopyTo(new DeltaCandleMessage { DeltaThreshold = DeltaThreshold, CurrentDelta = CurrentDelta }); } /// <summary> /// Kerzenparameter. /// </summary> public override object Arg { get => DeltaThreshold; set => DeltaThreshold = (decimal)value; } /// <summary> /// Typ des Kerzenarguments. /// </summary> public override Type ArgType => typeof(decimal); }Dann müssen Sie Ihren eigenen Datentyp in der Klasse DataType erstellen:
public static class DeltaCandleHelper { /// <summary> /// Eindeutigen MessageType für Delta-Kerzen definieren. /// </summary> public const MessageTypes DeltaCandleType = (MessageTypes)10001; /// <summary> /// Datentyp <see cref="DeltaCandleMessage"/>. /// </summary> public static readonly DataType CandleDelta = DataType.Create(typeof(DeltaCandleMessage)).Immutable(); /// <summary> /// Datentyp für Delta-Kerzen erstellen. /// </summary> /// <param name="threshold">Delta-Schwellenwert.</param> /// <returns>Datentyp.</returns> public static DataType Delta(this decimal threshold) { return DataType.Create(typeof(DeltaCandleMessage), threshold); } /// <summary> /// Delta-Kerzentyp im System registrieren. /// </summary> public static void RegisterDeltaCandleType() { // Neuen Kerzentyp in StockSharp registrieren Extensions.RegisterCandleType<decimal>( typeof(DeltaCandleMessage), // Kerzennachrichtentyp DeltaCandleType, // Nachrichtentyp "delta", // Dateiname für die Speicherung str => str.To<decimal>(), // Konverter von Zeichenfolge zu Parameter arg => arg.ToString(), // Konverter von Parameter zu Zeichenfolge a => a > 0, // Parametervalidator false // Ob solche Kerzen aus der Quelle bezogen werden können (nicht nur erstellt) ); } }Als Nächstes müssen Sie einen Kerzen-Builder für den neuen Typ erstellen. Erstellen Sie dazu eine Implementierung von CandleBuilder<TCandleMessage>:
/// <summary> /// Kerzen-Builder für den Typ <see cref="DeltaCandleMessage"/>. /// </summary> public class DeltaCandleBuilder : CandleBuilder<DeltaCandleMessage> { /// <summary> /// Initialisiert eine neue Instanz von <see cref="DeltaCandleBuilder"/>. /// </summary> /// <param name="exchangeInfoProvider">Börseninformationsprovider.</param> public DeltaCandleBuilder(IExchangeInfoProvider exchangeInfoProvider) : base(exchangeInfoProvider) { } /// <inheritdoc /> protected override DeltaCandleMessage CreateCandle(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform) { var time = transform.Time; return FirstInitCandle(subscription, new DeltaCandleMessage { DeltaThreshold = subscription.Message.GetArg<decimal>(), OpenTime = time, CloseTime = time, HighTime = time, LowTime = time, CurrentDelta = 0 }, transform); } /// <inheritdoc /> protected override bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, DeltaCandleMessage candle, ICandleBuilderValueTransform transform) { // Die Kerze wird geschlossen, wenn der absolute Delta-Wert den Schwellenwert überschreitet return Math.Abs(candle.CurrentDelta) >= candle.DeltaThreshold; } /// <inheritdoc /> protected override void UpdateCandle(ICandleBuilderSubscription subscription, DeltaCandleMessage candle, ICandleBuilderValueTransform transform) { base.UpdateCandle(subscription, candle, transform); // Delta anhand der Trade-Seite aktualisieren if (transform.Side == Sides.Buy) candle.CurrentDelta += transform.Volume ?? 0; else if (transform.Side == Sides.Sell) candle.CurrentDelta -= transform.Volume ?? 0; } }Dann müssen Sie den Kerzen-Builder in CandleBuilderProvider registrieren:
private Connector _connector; ... // Delta-Kerzentyp im System registrieren DeltaCandleHelper.RegisterDeltaCandleType(); // Delta-Kerzen-Builder registrieren _connector.Adapter.CandleBuilderProvider.Register(new DeltaCandleBuilder(_connector.ExchangeInfoProvider));Erstellen Sie ein Abonnement für Kerzen vom Typ
DeltaCandleMessageund fordern Sie Daten dafür an:// Delta-Schwellenwert decimal deltaThreshold = 1000m; // Abonnement für Delta-Kerzen erstellen var subscription = new Subscription( // Unsere Erweiterungsmethode zum Erstellen eines Datentyps verwenden deltaThreshold.Delta(), security) { MarketData = { // Angeben, dass Kerzen aus Ticks erstellt werden BuildMode = MarketDataBuildModes.Build, BuildFrom = DataType.Ticks } }; // Ereignis zum Empfang einer Kerze abonnieren _connector.CandleReceived += (sub, candle) => { if (sub != subscription) return; var deltaCandle = (DeltaCandleMessage)candle; // Delta-Kerze verarbeiten Console.WriteLine($"Delta-Kerze {candle.OpenTime}: O:{candle.OpenPrice} H:{candle.HighPrice} " + $"L:{candle.LowPrice} C:{candle.ClosePrice} V:{candle.TotalVolume} Delta:{deltaCandle.CurrentDelta}"); }; // Übergang in den Online-Modus abonnieren _connector.SubscriptionOnline += sub => { if (sub == subscription) Console.WriteLine("Delta-Kerzen-Abonnement ist in den Online-Modus gewechselt"); }; // Abonnement starten _connector.Subscribe(subscription);
Verwendung von Delta-Kerzen in Handelsstrategien
Beispiel einer einfachen Strategie mit Delta-Kerzen:
public class DeltaCandleStrategy : Strategy
{
private readonly StrategyParam<decimal> _deltaThreshold;
private readonly StrategyParam<decimal> _volume;
private readonly StrategyParam<decimal> _signalDelta;
private IChart _chart;
private IChartCandleElement _chartCandleElement;
private IChartIndicatorElement _deltaIndicatorElement;
public decimal DeltaThreshold
{
get => _deltaThreshold.Value;
set => _deltaThreshold.Value = value;
}
public decimal Volume
{
get => _volume.Value;
set => _volume.Value = value;
}
public decimal SignalDelta
{
get => _signalDelta.Value;
set => _signalDelta.Value = value;
}
public DeltaCandleStrategy()
{
// Strategieparameter
_deltaThreshold = Param(nameof(DeltaThreshold), 1000m)
.SetDisplay("Delta-Schwellenwert", "Volumendelta-Wert für die Kerzenbildung", "Haupteinstellungen")
.SetGreaterThanZero()
.SetCanOptimize(true)
.SetOptimize(500m, 2000m, 100m);
_volume = Param(nameof(Volume), 1m)
.SetDisplay("Ordervolumen", "Volumen für Handelsoperationen", "Haupteinstellungen")
.SetGreaterThanZero();
_signalDelta = Param(nameof(SignalDelta), 500m)
.SetDisplay("Mindestdelta für Signal", "Mindestdelta-Wert für die Signalerzeugung", "Haupteinstellungen")
.SetGreaterThanZero()
.SetCanOptimize(true);
Name = "DeltaCandleStrategy";
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Diagramm initialisieren, falls verfügbar
_chart = GetChart();
if (_chart != null)
{
var area = _chart.AddArea();
_chartCandleElement = area.AddCandles();
_deltaIndicatorElement = area.AddIndicator();
_deltaIndicatorElement.DrawStyle = DrawStyles.Histogram;
_deltaIndicatorElement.Color = System.Drawing.Color.Purple;
}
// Abonnement für Delta-Kerzen erstellen
var subscription = new Subscription(DeltaThreshold.Delta(), Security)
{
MarketData =
{
BuildMode = MarketDataBuildModes.Build,
BuildFrom = DataType.Ticks
}
};
// Regel zur Verarbeitung von Delta-Kerzen erstellen
this
.WhenCandleReceived(subscription)
.Do(ProcessDeltaCandle)
.Apply(this);
// Abonnement starten
Subscribe(subscription);
}
private void ProcessDeltaCandle(ICandleMessage candle)
{
// Im Diagramm zeichnen, falls verfügbar
if (_chart != null)
{
var deltaCandle = (DeltaCandleMessage)candle;
var data = _chart.CreateData();
data.Group(candle.OpenTime)
.Add(_chartCandleElement, candle)
.Add(_deltaIndicatorElement, deltaCandle.CurrentDelta);
_chart.Draw(data);
}
// Nur abgeschlossene Kerzen verarbeiten
if (candle.State != CandleStates.Finished)
return;
var deltaCandle = (DeltaCandleMessage)candle;
// Prüfen, ob das Delta für ein Signal ausreicht
if (Math.Abs(deltaCandle.CurrentDelta) < SignalDelta)
{
this.AddInfoLog($"Delta {deltaCandle.CurrentDelta} liegt unter dem Schwellenwert {SignalDelta}. Es wird kein Signal erzeugt.");
return;
}
// Operationsrichtung hängt vom Vorzeichen des Deltas ab
var direction = deltaCandle.CurrentDelta > 0 ? Sides.Buy : Sides.Sell;
this.AddInfoLog($"Delta-Kerze abgeschlossen. Delta: {deltaCandle.CurrentDelta}. Richtung: {direction}");
// Schlusskurs der Kerze zur Preisbestimmung verwenden
var price = deltaCandle.ClosePrice;
var volume = Volume;
// Wenn bereits eine Position in Gegenrichtung vorhanden ist,
// Volumen erhöhen, um die bestehende Position zu schließen
if ((Position < 0 && direction == Sides.Buy) ||
(Position > 0 && direction == Sides.Sell))
{
volume = Math.Max(volume, Math.Abs(Position) + volume);
}
// Order registrieren
RegisterOrder(this.CreateOrder(direction, price, volume));
}
}
Wichtige Punkte bei der Erstellung benutzerdefinierter Kerzentypen
Eindeutigkeit von MessageTypes — stellen Sie sicher, dass der von Ihnen gewählte
MessageTypes-Bezeichner nicht mit vorhandenen Typen in StockSharp in Konflikt steht. Es wird empfohlen, für benutzerdefinierte Typen Werte größer als 10000 zu verwenden.Registrierung des Kerzentyps — die Registrierung über
Extensions.RegisterCandleTypeist für die korrekte Integration mit den grafischen Steuerelementen und dem Datenspeicher von StockSharp erforderlich. Ohne Registrierung funktioniert der Kerzentyp nur im Code, ist aber nicht in der Benutzeroberfläche verfügbar.Kerzenparameter — implementieren Sie die Eigenschaft
ArgType, die den Typ des Kerzenarguments zurückgibt. Dies wird für die korrekte Anzeige von Parametern in der grafischen Benutzeroberfläche verwendet.Dateisystem — der Parameter
fileNamein der MethodeRegisterCandleTypewird verwendet, um Kerzen im Dateisystem zu speichern, wenn Sie den StockSharp-Datenspeicher verwenden.Parametervalidierung — die Methode zur Parametervalidierung wird in StockSharp verwendet, um die Korrektheit der Werte vor der Erstellung eines Abonnements zu überprüfen.
Somit haben wir einen vollständig benutzerdefinierten Kerzentyp erstellt, der ordnungsgemäß in das gesamte StockSharp-Ökosystem (einschließlich der Benutzeroberfläche und des Datenspeichers) integriert ist und zur Erstellung von Handelsstrategien basierend auf der Analyse des Volumendeltas verwendet werden kann.