Стратегия AI Supertrend Pivot Percentile
Стратегия сочетает два индикатора Supertrend с фильтром ADX и фильтром Williams %R по пивотным перцентилям. Длинная позиция открывается, когда цена выше обоих Supertrend, ADX подтверждает сильный тренд и Williams %R выше -50. Для короткой позиции применяются обратные условия.
Подробности
- Условия входа:
- Long: Цена выше обоих Supertrend, ADX > порога, Williams %R > -50.
- Short: Цена ниже обоих Supertrend, ADX > порога, Williams %R < -50.
- Long/Short: Обе стороны.
- Условия выхода:
- Противоположный сигнал.
- Стопы: Тейк-профит и стоп-лосс в процентах.
- Параметры по умолчанию:
Length1= 10Factor1= 3Length2= 20Factor2= 4AdxLength= 14AdxThreshold= 20PivotLength= 14TpPercent= 2SlPercent= 1
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: SuperTrend, ADX, Williams %R
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// AI Supertrend x Pivot Percentile Strategy - combines two Supertrend indicators
/// with ADX and Williams %R filters.
/// </summary>
public class AiSupertrendPivotPercentileStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length1;
private readonly StrategyParam<decimal> _factor1;
private readonly StrategyParam<int> _length2;
private readonly StrategyParam<decimal> _factor2;
private readonly StrategyParam<int> _adxLength;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _pivotLength;
private readonly StrategyParam<int> _cooldownBars;
private decimal _entryPrice;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Length1 { get => _length1.Value; set => _length1.Value = value; }
public decimal Factor1 { get => _factor1.Value; set => _factor1.Value = value; }
public int Length2 { get => _length2.Value; set => _length2.Value = value; }
public decimal Factor2 { get => _factor2.Value; set => _factor2.Value = value; }
public int AdxLength { get => _adxLength.Value; set => _adxLength.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public int PivotLength { get => _pivotLength.Value; set => _pivotLength.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AiSupertrendPivotPercentileStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_length1 = Param(nameof(Length1), 10)
.SetGreaterThanZero()
.SetDisplay("ST1 Length", "First Supertrend ATR length", "Supertrend");
_factor1 = Param(nameof(Factor1), 3m)
.SetGreaterThanZero()
.SetDisplay("ST1 Factor", "First Supertrend multiplier", "Supertrend");
_length2 = Param(nameof(Length2), 20)
.SetGreaterThanZero()
.SetDisplay("ST2 Length", "Second Supertrend ATR length", "Supertrend");
_factor2 = Param(nameof(Factor2), 4m)
.SetGreaterThanZero()
.SetDisplay("ST2 Factor", "Second Supertrend multiplier", "Supertrend");
_adxLength = Param(nameof(AdxLength), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Length", "ADX calculation period", "Filter");
_adxThreshold = Param(nameof(AdxThreshold), 15m)
.SetDisplay("ADX Threshold", "Minimum ADX for trading", "Filter");
_pivotLength = Param(nameof(PivotLength), 14)
.SetGreaterThanZero()
.SetDisplay("Pivot Length", "Length for Williams %R", "Filter");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var st1 = new SuperTrend { Length = Length1, Multiplier = Factor1 };
var st2 = new SuperTrend { Length = Length2, Multiplier = Factor2 };
var adx = new AverageDirectionalIndex { Length = AdxLength };
var wpr = new WilliamsR { Length = PivotLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(st1, st2, adx, wpr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, st1);
DrawIndicator(area, st2);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle,
IIndicatorValue st1Value,
IIndicatorValue st2Value,
IIndicatorValue adxValue,
IIndicatorValue wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var st1 = (SuperTrendIndicatorValue)st1Value;
var st2 = (SuperTrendIndicatorValue)st2Value;
var adxTyped = (IAverageDirectionalIndexValue)adxValue;
var wpr = wprValue.ToDecimal();
if (adxTyped.MovingAverage is not decimal adxMa)
return;
var st1Val = st1Value.ToDecimal();
var st2Val = st2Value.ToDecimal();
var isBull = candle.ClosePrice > st1Val && candle.ClosePrice > st2Val;
var isBear = candle.ClosePrice < st1Val && candle.ClosePrice < st2Val;
var strongTrend = adxMa > AdxThreshold;
var pivotBull = wpr > -50m;
var pivotBear = wpr < -50m;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
if (isBull && strongTrend && pivotBull && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
else if (isBear && strongTrend && pivotBear && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
// Exit conditions
else if (Position > 0 && (!isBull || !pivotBull))
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
else if (Position < 0 && (!isBear || !pivotBear))
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SuperTrend, AverageDirectionalIndex, WilliamsR, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class ai_supertrend_pivot_percentile_strategy(Strategy):
def __init__(self):
super(ai_supertrend_pivot_percentile_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._length1 = self.Param("Length1", 10) \
.SetGreaterThanZero() \
.SetDisplay("ST1 Length", "First Supertrend ATR length", "Supertrend")
self._factor1 = self.Param("Factor1", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("ST1 Factor", "First Supertrend multiplier", "Supertrend")
self._length2 = self.Param("Length2", 20) \
.SetGreaterThanZero() \
.SetDisplay("ST2 Length", "Second Supertrend ATR length", "Supertrend")
self._factor2 = self.Param("Factor2", 4.0) \
.SetGreaterThanZero() \
.SetDisplay("ST2 Factor", "Second Supertrend multiplier", "Supertrend")
self._adx_length = self.Param("AdxLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("ADX Length", "ADX calculation period", "Filter")
self._adx_threshold = self.Param("AdxThreshold", 15.0) \
.SetDisplay("ADX Threshold", "Minimum ADX for trading", "Filter")
self._pivot_length = self.Param("PivotLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("Pivot Length", "Length for Williams %R", "Filter")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._entry_price = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ai_supertrend_pivot_percentile_strategy, self).OnReseted()
self._entry_price = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ai_supertrend_pivot_percentile_strategy, self).OnStarted2(time)
st1 = SuperTrend()
st1.Length = int(self._length1.Value)
st1.Multiplier = self._factor1.Value
st2 = SuperTrend()
st2.Length = int(self._length2.Value)
st2.Multiplier = self._factor2.Value
adx = AverageDirectionalIndex()
adx.Length = int(self._adx_length.Value)
wpr = WilliamsR()
wpr.Length = int(self._pivot_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(st1, st2, adx, wpr, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, st1)
self.DrawIndicator(area, st2)
self.DrawOwnTrades(area)
def _on_process(self, candle, st1_value, st2_value, adx_value, wpr_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
adx_v = float(adx_ma)
wpr_v = float(IndicatorHelper.ToDecimal(wpr_value))
st1_v = float(IndicatorHelper.ToDecimal(st1_value))
st2_v = float(IndicatorHelper.ToDecimal(st2_value))
close = float(candle.ClosePrice)
threshold = float(self._adx_threshold.Value)
cooldown = int(self._cooldown_bars.Value)
is_bull = close > st1_v and close > st2_v
is_bear = close < st1_v and close < st2_v
strong_trend = adx_v > threshold
pivot_bull = wpr_v > -50.0
pivot_bear = wpr_v < -50.0
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
if is_bull and strong_trend and pivot_bull and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
elif is_bear and strong_trend and pivot_bear and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
elif self.Position > 0 and (not is_bull or not pivot_bull):
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and (not is_bear or not pivot_bear):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return ai_supertrend_pivot_percentile_strategy()