Diese Strategie handelt auf Basis des Crossovers des True Strength Index (TSI), der aus dem Williams %R-Oszillator berechnet wird.
Wenn der TSI über seine geglättete Signallinie kreuzt, eröffnet die Strategie eine Long-Position. Wenn der TSI unter die Signallinie kreuzt, eröffnet sie eine Short-Position.
Parameter
Candle Type: Zeitrahmen der für die Berechnung verwendeten Kerzen.
Williams %R Period: Anzahl der Balken für den Williams %R-Indikator.
Short Length: Kurze EMA-Länge, die in der TSI-Berechnung verwendet wird.
Long Length: Lange EMA-Länge, die in der TSI-Berechnung verwendet wird.
Signal Length: EMA-Länge, die auf den TSI angewendet wird, um die Signallinie zu bilden.
Handelsregeln
Williams %R-Wert jeder abgeschlossenen Kerze berechnen.
Diesen Wert in den True Strength Index-Indikator einspeisen.
TSI mit einem EMA glätten, um die Signallinie zu erhalten.
Kaufen, wenn TSI über die Signallinie kreuzt.
Verkaufen, wenn TSI unter die Signallinie kreuzt.
Bestehende Positionen in entgegengesetzter Richtung werden bei einem neuen Signal geschlossen.
Hinweise
Die Strategie verwendet die High-Level-API mit automatischen Kerzenabonnements.
StartProtection wird beim Start für das grundlegende Risikomanagement gestartet.
Chartbereiche werden erstellt, um TSI, seine Signallinie und ausgeführte Trades zu visualisieren.
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>
/// Strategy based on True Strength Index crossover filtered by Williams %R.
/// Buys when TSI crosses above its signal line and WPR is in oversold,
/// sells when TSI crosses below its signal line and WPR is in overbought.
/// </summary>
public class TsiWprCrossStrategy : Strategy
{
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevTsi;
private decimal _prevSignal;
private bool _initialized;
private int _cooldownRemaining;
/// <summary>
/// Williams %R period.
/// </summary>
public int WprPeriod
{
get => _wprPeriod.Value;
set => _wprPeriod.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candles type to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public TsiWprCrossStrategy()
{
_wprPeriod = Param(nameof(WprPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a signal", "Signal");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevTsi = 0m;
_prevSignal = 0m;
_initialized = false;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var tsi = new TrueStrengthIndex();
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(tsi, wpr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, tsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue tsiValue, IIndicatorValue wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!tsiValue.IsFinal || !wprValue.IsFinal)
return;
var tv = (ITrueStrengthIndexValue)tsiValue;
if (tv.Tsi is not decimal tsi || tv.Signal is not decimal signal)
return;
var wpr = wprValue.ToDecimal();
if (!_initialized)
{
_prevTsi = tsi;
_prevSignal = signal;
_initialized = true;
return;
}
var crossedUp = _prevTsi <= _prevSignal && tsi > signal;
var crossedDown = _prevTsi >= _prevSignal && tsi < signal;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
// WPR range: -100 to 0. Oversold < -80, Overbought > -20
if (crossedUp && wpr < -55 && _cooldownRemaining == 0 && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossedDown && wpr > -45 && _cooldownRemaining == 0 && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
_prevTsi = tsi;
_prevSignal = signal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import TrueStrengthIndex, WilliamsR
from StockSharp.Algo.Strategies import Strategy
class tsi_wpr_cross_strategy(Strategy):
def __init__(self):
super(tsi_wpr_cross_strategy, self).__init__()
self._wpr_period = self.Param("WprPeriod", 14) \
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a signal", "Signal")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle type for strategy", "General")
self._prev_tsi = 0.0
self._prev_signal = 0.0
self._initialized = False
self._cooldown_remaining = 0
@property
def wpr_period(self):
return self._wpr_period.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(tsi_wpr_cross_strategy, self).OnReseted()
self._prev_tsi = 0.0
self._prev_signal = 0.0
self._initialized = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(tsi_wpr_cross_strategy, self).OnStarted2(time)
tsi = TrueStrengthIndex()
wpr = WilliamsR()
wpr.Length = self.wpr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(tsi, wpr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, tsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, tsi_value, wpr_value):
if candle.State != CandleStates.Finished:
return
if not tsi_value.IsFinal or not wpr_value.IsFinal:
return
tsi_val = tsi_value.Tsi
signal_val = tsi_value.Signal
if tsi_val is None or signal_val is None:
return
tsi_val = float(tsi_val)
signal_val = float(signal_val)
wpr = float(wpr_value)
if not self._initialized:
self._prev_tsi = tsi_val
self._prev_signal = signal_val
self._initialized = True
return
crossed_up = self._prev_tsi <= self._prev_signal and tsi_val > signal_val
crossed_down = self._prev_tsi >= self._prev_signal and tsi_val < signal_val
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if crossed_up and wpr < -55.0 and self._cooldown_remaining == 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif crossed_down and wpr > -45.0 and self._cooldown_remaining == 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_tsi = tsi_val
self._prev_signal = signal_val
def CreateClone(self):
return tsi_wpr_cross_strategy()