Vietnamese 3x Supertrend 策略
该策略使用三个不同 ATR 参数的 SuperTrend 指标。当慢速趋势仍为下行时,在回调处逐步建立多头头寸。价格向有利方向运行后,可选择启用保本止损。
细节
- 入场条件:
- 慢速 SuperTrend 为下行趋势。
- Long 1:中速趋势向上且快速趋势向下。
- Long 2:中速趋势向下且价格高于快速 SuperTrend 线。
- Long 3:快速趋势向下并向上突破该阶段的最高价。
- 多空方向:仅做多。
- 出场条件:
- 所有 SuperTrend 转为上行且 K 线收阴。
- 平均持仓价高于当前收盘价。
- 可选保本止损。
- 止损:可选保本止损。
- 默认参数:
FastAtrLength= 10FastMultiplier= 1MediumAtrLength= 11MediumMultiplier= 2SlowAtrLength= 12SlowMultiplier= 3UseHighestOfTwoRedCandles= falseUseEntryStopLoss= trueUseAllDowntrendExit= trueUseAvgPriceInLoss= true
- 过滤条件:
- 分类:趋势跟随
- 方向:多头
- 指标:SuperTrend
- 止损:可选
- 复杂度:中等
- 时间框架:任意
- 季节性:无
- 神经网络:无
- 背离:无
- 风险等级:中等
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Vietnamese 3x SuperTrend strategy.
/// Uses three SuperTrend indicators with different settings.
/// Enters long when SuperTrend conditions align.
/// Exits based on break-even, all-uptrend-red-candle, or avg price in loss.
/// </summary>
public class Vietnamese3xSupertrendStrategy : Strategy
{
private readonly StrategyParam<int> _fastAtrLength;
private readonly StrategyParam<decimal> _fastMultiplier;
private readonly StrategyParam<int> _mediumAtrLength;
private readonly StrategyParam<decimal> _mediumMultiplier;
private readonly StrategyParam<int> _slowAtrLength;
private readonly StrategyParam<decimal> _slowMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _highestGreen;
private bool _breakEvenActive;
private decimal _avgEntryPrice;
private int _entryCount;
private int _cooldownRemaining;
public int FastAtrLength { get => _fastAtrLength.Value; set => _fastAtrLength.Value = value; }
public decimal FastMultiplier { get => _fastMultiplier.Value; set => _fastMultiplier.Value = value; }
public int MediumAtrLength { get => _mediumAtrLength.Value; set => _mediumAtrLength.Value = value; }
public decimal MediumMultiplier { get => _mediumMultiplier.Value; set => _mediumMultiplier.Value = value; }
public int SlowAtrLength { get => _slowAtrLength.Value; set => _slowAtrLength.Value = value; }
public decimal SlowMultiplier { get => _slowMultiplier.Value; set => _slowMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public Vietnamese3xSupertrendStrategy()
{
_fastAtrLength = Param(nameof(FastAtrLength), 10)
.SetDisplay("Fast ATR Length", "ATR length for fast SuperTrend", "SuperTrend");
_fastMultiplier = Param(nameof(FastMultiplier), 1m)
.SetDisplay("Fast Multiplier", "ATR multiplier for fast SuperTrend", "SuperTrend");
_mediumAtrLength = Param(nameof(MediumAtrLength), 11)
.SetDisplay("Medium ATR Length", "ATR length for medium SuperTrend", "SuperTrend");
_mediumMultiplier = Param(nameof(MediumMultiplier), 2m)
.SetDisplay("Medium Multiplier", "ATR multiplier for medium SuperTrend", "SuperTrend");
_slowAtrLength = Param(nameof(SlowAtrLength), 12)
.SetDisplay("Slow ATR Length", "ATR length for slow SuperTrend", "SuperTrend");
_slowMultiplier = Param(nameof(SlowMultiplier), 3m)
.SetDisplay("Slow Multiplier", "ATR multiplier for slow SuperTrend", "SuperTrend");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_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();
_highestGreen = 0;
_breakEvenActive = false;
_avgEntryPrice = 0;
_entryCount = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new SuperTrend { Length = FastAtrLength, Multiplier = FastMultiplier };
var medium = new SuperTrend { Length = MediumAtrLength, Multiplier = MediumMultiplier };
var slow = new SuperTrend { Length = SlowAtrLength, Multiplier = SlowMultiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fast, medium, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastVal, IIndicatorValue medVal, IIndicatorValue slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var fastSt = (SuperTrendIndicatorValue)fastVal;
var medSt = (SuperTrendIndicatorValue)medVal;
var slowSt = (SuperTrendIndicatorValue)slowVal;
var dir1 = fastSt.IsUpTrend ? 1 : -1;
var dir2 = medSt.IsUpTrend ? 1 : -1;
var dir3 = slowSt.IsUpTrend ? 1 : -1;
// Track highest green candle for breakout entry
if (dir1 < 0 && _highestGreen == 0)
_highestGreen = candle.HighPrice;
if (_highestGreen > 0 && dir1 < 0)
_highestGreen = Math.Max(_highestGreen, candle.HighPrice);
if (dir1 >= 0)
_highestGreen = 0;
// Exit logic for longs
if (Position > 0)
{
// Break-even stop
if (dir1 > 0 && dir2 < 0 && dir3 < 0)
{
if (!_breakEvenActive && candle.LowPrice > _avgEntryPrice)
_breakEvenActive = true;
if (_breakEvenActive && candle.LowPrice <= _avgEntryPrice)
{
SellMarket(Math.Abs(Position));
ResetEntries();
_cooldownRemaining = CooldownBars;
return;
}
}
// All uptrend + red candle exit
if (dir3 > 0 && dir2 > 0 && dir1 > 0 && candle.ClosePrice < candle.OpenPrice)
{
SellMarket(Math.Abs(Position));
ResetEntries();
_cooldownRemaining = CooldownBars;
return;
}
// Avg price in loss exit
if (_avgEntryPrice > candle.ClosePrice)
{
SellMarket(Math.Abs(Position));
ResetEntries();
_cooldownRemaining = CooldownBars;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Entry logic - max 3 entries
if (_entryCount < 3)
{
if (dir3 < 0)
{
if (dir2 > 0 && dir1 < 0)
{
BuyMarket(Volume);
AddEntry(candle.ClosePrice);
_cooldownRemaining = CooldownBars;
}
else if (dir2 < 0 && candle.ClosePrice > fastSt.Value)
{
BuyMarket(Volume);
AddEntry(candle.ClosePrice);
_cooldownRemaining = CooldownBars;
}
}
else
{
if (dir1 < 0 && _highestGreen > 0 && candle.ClosePrice > _highestGreen)
{
BuyMarket(Volume);
AddEntry(candle.ClosePrice);
_cooldownRemaining = CooldownBars;
}
}
}
}
private void AddEntry(decimal price)
{
_avgEntryPrice = (_avgEntryPrice * _entryCount + price) / (_entryCount + 1);
_entryCount++;
}
private void ResetEntries()
{
_avgEntryPrice = 0;
_entryCount = 0;
_breakEvenActive = false;
}
}
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, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class vietnamese_3x_supertrend_strategy(Strategy):
"""Vietnamese 3x SuperTrend Strategy."""
def __init__(self):
super(vietnamese_3x_supertrend_strategy, self).__init__()
self._fast_atr_length = self.Param("FastAtrLength", 10) \
.SetDisplay("Fast ATR Length", "ATR length for fast SuperTrend", "SuperTrend")
self._fast_multiplier = self.Param("FastMultiplier", 1.0) \
.SetDisplay("Fast Multiplier", "ATR multiplier for fast SuperTrend", "SuperTrend")
self._medium_atr_length = self.Param("MediumAtrLength", 11) \
.SetDisplay("Medium ATR Length", "ATR length for medium SuperTrend", "SuperTrend")
self._medium_multiplier = self.Param("MediumMultiplier", 2.0) \
.SetDisplay("Medium Multiplier", "ATR multiplier for medium SuperTrend", "SuperTrend")
self._slow_atr_length = self.Param("SlowAtrLength", 12) \
.SetDisplay("Slow ATR Length", "ATR length for slow SuperTrend", "SuperTrend")
self._slow_multiplier = self.Param("SlowMultiplier", 3.0) \
.SetDisplay("Slow Multiplier", "ATR multiplier for slow SuperTrend", "SuperTrend")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._highest_green = 0.0
self._break_even_active = False
self._avg_entry_price = 0.0
self._entry_count = 0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vietnamese_3x_supertrend_strategy, self).OnReseted()
self._highest_green = 0.0
self._break_even_active = False
self._avg_entry_price = 0.0
self._entry_count = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(vietnamese_3x_supertrend_strategy, self).OnStarted2(time)
fast = SuperTrend()
fast.Length = int(self._fast_atr_length.Value)
fast.Multiplier = self._fast_multiplier.Value
medium = SuperTrend()
medium.Length = int(self._medium_atr_length.Value)
medium.Multiplier = self._medium_multiplier.Value
slow = SuperTrend()
slow.Length = int(self._slow_atr_length.Value)
slow.Multiplier = self._slow_multiplier.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(fast, medium, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_val, med_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
dir1 = 1 if fast_val.IsUpTrend else -1
dir2 = 1 if med_val.IsUpTrend else -1
dir3 = 1 if slow_val.IsUpTrend else -1
high_price = float(candle.HighPrice)
close_price = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
low_price = float(candle.LowPrice)
cooldown = int(self._cooldown_bars.Value)
# Track highest green candle for breakout entry
if dir1 < 0 and self._highest_green == 0.0:
self._highest_green = high_price
if self._highest_green > 0 and dir1 < 0:
self._highest_green = max(self._highest_green, high_price)
if dir1 >= 0:
self._highest_green = 0.0
# Exit logic for longs
if self.Position > 0:
# Break-even stop
if dir1 > 0 and dir2 < 0 and dir3 < 0:
if not self._break_even_active and low_price > self._avg_entry_price:
self._break_even_active = True
if self._break_even_active and low_price <= self._avg_entry_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_entries()
self._cooldown_remaining = cooldown
return
# All uptrend + red candle exit
if dir3 > 0 and dir2 > 0 and dir1 > 0 and close_price < open_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_entries()
self._cooldown_remaining = cooldown
return
# Avg price in loss exit
if self._avg_entry_price > close_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_entries()
self._cooldown_remaining = cooldown
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
# Entry logic - max 3 entries
if self._entry_count < 3:
if dir3 < 0:
if dir2 > 0 and dir1 < 0:
self.BuyMarket(self.Volume)
self._add_entry(close_price)
self._cooldown_remaining = cooldown
elif dir2 < 0 and close_price > float(IndicatorHelper.ToDecimal(fast_val)):
self.BuyMarket(self.Volume)
self._add_entry(close_price)
self._cooldown_remaining = cooldown
else:
if dir1 < 0 and self._highest_green > 0 and close_price > self._highest_green:
self.BuyMarket(self.Volume)
self._add_entry(close_price)
self._cooldown_remaining = cooldown
def _add_entry(self, price):
self._avg_entry_price = (self._avg_entry_price * self._entry_count + price) / (self._entry_count + 1)
self._entry_count += 1
def _reset_entries(self):
self._avg_entry_price = 0.0
self._entry_count = 0
self._break_even_active = False
def CreateClone(self):
return vietnamese_3x_supertrend_strategy()