Auf GitHub ansehen

Exp Color TSI Oszillator-Strategie

Übersicht

  • Konvertierung des MetaTrader 5 Expertenberaters Exp_ColorTSI-Oscillator in das StockSharp-Framework.
  • Rekonstruiert den ColorTSI-Oszillator: einen doppelt geglätteten True Strength Index mit einer verzögerten Trigger-Linie und mehreren Glättungsalgorithmen aus SmoothAlgorithms.mqh.
  • Generiert Trades, wenn der Oszillator gegenüber seinem verzögerten Trigger auf- oder abwärts dreht, was den "Swing-Umkehr"-Stil des ursprünglichen EA repliziert.

Indikatorrekonstruktion

  • Der angewendete Preis wird über die Option ColorTsiAppliedPrice ausgewählt (Close, Open, Median, Typical, Weighted, Demark usw.).
  • Preis-Momentum (diff = price[n] - price[n-1]) und sein Absolutwert werden in zwei Stufen geglättet:
    1. Erste Stufe: konfigurierbarer ColorTsiSmoothingMethod (Sma, Ema, Smma, Lwma, Jjma, Jurx, Parma, T3, Vidya, Ama) mit Länge FirstLength und Phase FirstPhase für Jurik-ähnliche Filter.
    2. Zweite Stufe: identische Methodenoptionen mit SecondLength/SecondPhase, angewendet auf die bereits geglättete Momentum-Reihe.
  • Die Oszillator-Ausgabe ist TSI = 100 * smoothMomentum / smoothAbsMomentum. Wenn der Nenner null ist, wird der Wert ignoriert.
  • Eine Trigger-Linie wird durch Verzögerung des TSI um TriggerShift Balken erhalten, was die MetaTrader-Buffer-Logik widerspiegelt.
  • Historische Werte werden gespeichert, damit SignalBar dem MetaTrader-CopyBuffer-Zugriffsmuster entspricht (Index SignalBar = zuletzt geschlossener Balken, SignalBar + 1 = vorheriger Balken, usw.).

Handelsregeln

  • Berechnungen laufen auf abgeschlossenen Kerzen, die von CandleType geliefert werden (Standard: 4-Stunden-Zeitrahmen).
  • Sei TSI[k] der Oszillatorwert und Trigger[k] die verzögerte Reihe.
  • Bullischer Kontext: TSI[SignalBar + 1] > Trigger[SignalBar + 1] ⇒ der vorherige Balken zeigte aufwärtsgerichtetes Momentum.
    • Shorts schließen, wenn EnableShortExits true ist.
    • Eine Long-Position eröffnen, wenn EnableLongEntries true ist und TSI[SignalBar] ≤ Trigger[SignalBar], was eine Aufwärtsschwankung nach dem Rücksetzer signalisiert.
  • Bärischer Kontext: TSI[SignalBar + 1] < Trigger[SignalBar + 1] ⇒ der vorherige Balken zeigte abwärtsgerichtetes Momentum.
    • Longs schließen, wenn EnableLongExits true ist.
    • Eine Short-Position eröffnen, wenn EnableShortEntries true ist und TSI[SignalBar] ≥ Trigger[SignalBar].
  • Einstiegssignale werden durch die Zeit des analysierten Balkens plus einem vollen Zeitrahmen kodiert; jedes Signal kann dank _lastLongEntryTime / _lastShortEntryTime-Wächtern höchstens einen Trade auslösen.
  • Alle Aktionen werden mit Market-Orders ausgeführt. Bestehende entgegengerichtete Positionen werden vor Umkehrungen geschlossen.

Parameter

Parameter Beschreibung Standard
CandleType Datenstrom für die Analyse. Unterstützt jeden DataType (Zeit-, Tick-, Volumen-Kerzen). H4-Zeitrahmen
Volume Feste Ordergröße, die die Geldverwaltungsblöcke des EA ersetzt. Muss > 0 sein. 0.1
FirstMethod, FirstLength, FirstPhase Erste Glättungsstufe für Momentum und absolutes Momentum. SMA, 12, 15
SecondMethod, SecondLength, SecondPhase Zweite Glättungsstufe. SMA, 12, 15
PriceMode Angewendete Preisoption für den Oszillator. Close
SignalBar Balkenversatz zur Signalauswertung (1 = letzter geschlossener Balken). 1
TriggerShift Verzögerung der Trigger-Linie (1 reproduziert den ursprünglichen Indikator). 1
EnableLongEntries / EnableShortEntries Erlaubt das Eröffnen von Long-/Short-Trades. true
EnableLongExits / EnableShortExits Erlaubt das Schließen von Positionen bei entgegengesetztem Kontext. true
StopLossPoints Stop-Loss-Abstand in Preispunkten (umgerechnet mit dem Instrument-PriceStep). 1000
TakeProfitPoints Take-Profit-Abstand in Preispunkten. 2000

Risikomanagement

  • Der ursprüngliche EA verwendete Hilfsfunktionen aus TradeAlgorithms.mqh für die SL/TP-Platzierung. Die C#-Version ruft StartProtection mit den ausgewählten Abständen auf, die in UnitTypes.Point konvertiert wurden.
  • Wenn ein Abstand auf 0 gesetzt wird, wird die entsprechende Schutzorder ausgelassen.
  • Keine Trailing-Stops oder Positionsskalierung sind implementiert; diese entsprechen dem MetaTrader-Verhalten für diesen Experten.

Unterschiede zur MetaTrader-Version

  • Marginsbasiertes Lot-Sizing (MM und MMMode) wird durch einen festen Volume-Parameter ersetzt. Dies hält das Verhalten broker-übergreifend deterministisch und vermeidet die Replikation kontospezifischer Hebellogik.
  • Slippage (Deviation_) wird nicht emuliert, da StockSharp-Market-Orders keinen Slippage-Parameter exponieren.
  • Indikatorglättung wird vollständig mit StockSharp-Indikatoren rekonstruiert (einschließlich Jurik-Phasenbehandlung durch Reflection), sodass Signalwerte mit den ursprünglichen Buffern übereinstimmen.
  • Python-Implementierung wird absichtlich weggelassen, wie angefordert.

Verwendungshinweise

  • Sicherstellen, dass das ausgewählte Instrument den von CandleType angeforderten Kerzentyp bereitstellt. Für Standard-Zeitrahmen TimeSpan.FromHours(x).TimeFrame() verwenden.
  • SignalBar muss ≥ TriggerShift sein, um gültige Trigger-Werte zu erhalten; andernfalls werden Signale übersprungen, bis genügend Historie angesammelt ist.
  • Da die Strategie auf abgeschlossenen Kerzen reagiert, Echtzeit-Order-Registrierung nur nach IsFormedAndOnlineAndAllowTrading() aktivieren.
  • Der Chart-Bereich visualisiert Preiskerzen und ausgeführte Trades; Indikatoren werden intern rekonstruiert und nicht automatisch geplottet.
  • Zum Reproduzieren der MetaTrader-Standards: alle Glättungseinstellungen auf SMA mit Länge 12 belassen, beide Einstiegs- und Ausstiegs-Toggles aktiviert lassen und Standard-Stop/Take-Abstände verwenden.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Color TSI Oscillator strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExpColorTsiOscillatorStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ExpColorTsiOscillatorStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}