Double Supertrend
Double Supertrend 使用两个基于 ATR 的移动平均线,周期和倍数各不相同。 第一条线确定交易方向,第二条线可作为目标或跟踪退出,从而在趋势交易中 提供灵活的盈亏控制。
当价格位于两条线之上且允许做多时开多单;做空条件类似。退出依据所选 的止盈类型或百分比止损。
细节
- 数据: 价格K线。
- 入场条件: 价格按允许的
Direction穿越 Supertrend 线。 - 离场条件: 破坏相反线、触发止盈 (
TPType/TPPercent) 或止损 (SLPercent)。 - 止损: 按价格百分比的止损 (
SLPercent)。 - 默认参数:
ATRPeriod1= 10Factor1= 3.0ATRPeriod2= 20Factor2= 5.0Direction= "Long"TPType= "Supertrend"TPPercent= 1.5SLPercent= 10.0
- 过滤器:
- 类型: 趋势跟随
- 方向: 可配置
- 指标: 基于 ATR 的 Supertrend
- 复杂度: 高
- 风险级别: 中等
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>
/// Double Supertrend Strategy.
/// Uses two SuperTrend indicators with different parameters.
/// Enters long when both SuperTrends are bullish.
/// Enters short when both SuperTrends are bearish.
/// </summary>
public class DoubleSupertrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _atrPeriod1;
private readonly StrategyParam<decimal> _factor1;
private readonly StrategyParam<int> _atrPeriod2;
private readonly StrategyParam<decimal> _factor2;
private readonly StrategyParam<int> _cooldownBars;
private SuperTrend _st1;
private SuperTrend _st2;
private bool _prevUpTrend1;
private bool _prevUpTrend2;
private bool _hasPrev;
private int _cooldownRemaining;
public DoubleSupertrendStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_atrPeriod1 = Param(nameof(ATRPeriod1), 10)
.SetGreaterThanZero()
.SetDisplay("ST1 Period", "First SuperTrend ATR period", "SuperTrend 1");
_factor1 = Param(nameof(Factor1), 2.0m)
.SetDisplay("ST1 Factor", "First SuperTrend multiplier", "SuperTrend 1");
_atrPeriod2 = Param(nameof(ATRPeriod2), 20)
.SetGreaterThanZero()
.SetDisplay("ST2 Period", "Second SuperTrend ATR period", "SuperTrend 2");
_factor2 = Param(nameof(Factor2), 4.0m)
.SetDisplay("ST2 Factor", "Second SuperTrend multiplier", "SuperTrend 2");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int ATRPeriod1
{
get => _atrPeriod1.Value;
set => _atrPeriod1.Value = value;
}
public decimal Factor1
{
get => _factor1.Value;
set => _factor1.Value = value;
}
public int ATRPeriod2
{
get => _atrPeriod2.Value;
set => _atrPeriod2.Value = value;
}
public decimal Factor2
{
get => _factor2.Value;
set => _factor2.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_st1 = null;
_st2 = null;
_prevUpTrend1 = false;
_prevUpTrend2 = false;
_hasPrev = false;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_st1 = new SuperTrend { Length = ATRPeriod1, Multiplier = Factor1 };
_st2 = new SuperTrend { Length = ATRPeriod2, Multiplier = Factor2 };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_st1, _st2, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _st1);
DrawIndicator(area, _st2);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue st1Value, IIndicatorValue st2Value)
{
if (candle.State != CandleStates.Finished)
return;
if (!_st1.IsFormed || !_st2.IsFormed)
return;
if (st1Value.IsEmpty || st2Value.IsEmpty)
return;
var stv1 = (SuperTrendIndicatorValue)st1Value;
var stv2 = (SuperTrendIndicatorValue)st2Value;
var upTrend1 = stv1.IsUpTrend;
var upTrend2 = stv2.IsUpTrend;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
_hasPrev = true;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
_hasPrev = true;
return;
}
if (!_hasPrev)
{
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
_hasPrev = true;
return;
}
// Both bullish
var bothBullish = upTrend1 && upTrend2;
// Both bearish
var bothBearish = !upTrend1 && !upTrend2;
// Trend changed to both bullish
var bullishSignal = bothBullish && (!_prevUpTrend1 || !_prevUpTrend2);
// Trend changed to both bearish
var bearishSignal = bothBearish && (_prevUpTrend1 || _prevUpTrend2);
// Buy when both SuperTrends turn bullish
if (bullishSignal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell when both SuperTrends turn bearish
else if (bearishSignal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long if either SuperTrend turns bearish
else if (Position > 0 && !bothBullish && (_prevUpTrend1 && _prevUpTrend2))
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short if either SuperTrend turns bullish
else if (Position < 0 && !bothBearish && (!_prevUpTrend1 && !_prevUpTrend2))
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class double_supertrend_strategy(Strategy):
"""Double SuperTrend Strategy.
Uses two SuperTrend indicators with different parameters.
Enters long when both SuperTrends are bullish.
Enters short when both SuperTrends are bearish."""
def __init__(self):
super(double_supertrend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._atr_period1 = self.Param("ATRPeriod1", 10) \
.SetDisplay("ST1 Period", "First SuperTrend ATR period", "SuperTrend 1")
self._factor1 = self.Param("Factor1", 2.0) \
.SetDisplay("ST1 Factor", "First SuperTrend multiplier", "SuperTrend 1")
self._atr_period2 = self.Param("ATRPeriod2", 20) \
.SetDisplay("ST2 Period", "Second SuperTrend ATR period", "SuperTrend 2")
self._factor2 = self.Param("Factor2", 4.0) \
.SetDisplay("ST2 Factor", "Second SuperTrend multiplier", "SuperTrend 2")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._st1 = None
self._st2 = None
self._prev_up_trend1 = False
self._prev_up_trend2 = False
self._has_prev = False
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(double_supertrend_strategy, self).OnReseted()
self._st1 = None
self._st2 = None
self._prev_up_trend1 = False
self._prev_up_trend2 = False
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(double_supertrend_strategy, self).OnStarted2(time)
self._st1 = SuperTrend()
self._st1.Length = int(self._atr_period1.Value)
self._st1.Multiplier = float(self._factor1.Value)
self._st2 = SuperTrend()
self._st2.Length = int(self._atr_period2.Value)
self._st2.Multiplier = float(self._factor2.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._st1, self._st2, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._st1)
self.DrawIndicator(area, self._st2)
self.DrawOwnTrades(area)
def _on_process(self, candle, st1_value, st2_value):
if candle.State != CandleStates.Finished:
return
if not self._st1.IsFormed or not self._st2.IsFormed:
return
if st1_value.IsEmpty or st2_value.IsEmpty:
return
up_trend1 = st1_value.IsUpTrend
up_trend2 = st2_value.IsUpTrend
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
self._has_prev = True
return
if not self._has_prev:
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
self._has_prev = True
return
cooldown = int(self._cooldown_bars.Value)
both_bullish = up_trend1 and up_trend2
both_bearish = not up_trend1 and not up_trend2
bullish_signal = both_bullish and (not self._prev_up_trend1 or not self._prev_up_trend2)
bearish_signal = both_bearish and (self._prev_up_trend1 or self._prev_up_trend2)
if bullish_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif bearish_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and not both_bullish and (self._prev_up_trend1 and self._prev_up_trend2):
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and not both_bearish and (not self._prev_up_trend1 and not self._prev_up_trend2):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
def CreateClone(self):
return double_supertrend_strategy()